For Each...Next — Instrukcja (Visual Basic)

Powtarza grupę instrukcji dla każdego elementu w kolekcji.

Składnia

For Each element [ As datatype ] In group
    [ statements ]
    [ Continue For ]
    [ statements ]
    [ Exit For ]
    [ statements ]
Next [ element ]

generatora

Termin Definicja
element Wymagane w instrukcji For Each . Opcjonalnie w instrukcji Next . Zmiennej. Służy do iterowania elementów kolekcji.
datatype Opcjonalnie, jeśli Option Infer wartość jest włączona (wartość domyślna) lub element jest już zadeklarowana; wymagana, jeśli Option Infer jest wyłączona i element nie jest jeszcze zadeklarowana. Typ danych .element
group Wymagany. Zmienna o typie kolekcji lub obiekcie. Odnosi się do kolekcji, w której statements mają być powtarzane.
statements Opcjonalny. Co najmniej jedna instrukcja między For EachNext i uruchamiana na każdym elemencie w elemencie group.
Continue For Opcjonalny. Przenosi kontrolkę na początek For Each pętli.
Exit For Opcjonalny. Przenosi kontrolkę poza pętlę For Each .
Next Wymagany. Przerywa definicję For Each pętli.

Prosty przykład

For EachUżyj pętli ...Next, jeśli chcesz powtórzyć zestaw instrukcji dla każdego elementu kolekcji lub tablicy.

Napiwek

A dla... Następna instrukcja sprawdza się dobrze, gdy można skojarzyć każdą iterację pętli ze zmienną kontrolną i określić początkowe i końcowe wartości tej zmiennej. Jednak jeśli masz do czynienia z kolekcją, koncepcja początkowych i końcowych wartości nie jest znacząca i nie musisz wiedzieć, ile elementów ma kolekcja. W takim przypadku pętla For Each...Next jest często lepszym wyborem.

W poniższym przykładzie ...For EachNext instrukcja iteruje wszystkie elementy kolekcji List.

' Create a list of strings by using a
' collection initializer.
Dim lst As New List(Of String) _
    From {"abc", "def", "ghi"}

' Iterate through the list.
For Each item As String In lst
    Debug.Write(item & " ")
Next
Debug.WriteLine("")
'Output: abc def ghi

Aby uzyskać więcej przykładów, zobacz Kolekcje i tablice.

Pętle zagnieżdżone

Pętle można zagnieżdżać For Each , umieszczając jedną pętlę w innej.

W poniższym przykładzie pokazano zagnieżdżone For Each...Next Struktur.

' Create lists of numbers and letters
' by using array initializers.
Dim numbers() As Integer = {1, 4, 7}
Dim letters() As String = {"a", "b", "c"}

' Iterate through the list by using nested loops.
For Each number As Integer In numbers
    For Each letter As String In letters
        Debug.Write(number.ToString & letter & " ")
    Next
Next
Debug.WriteLine("")
'Output: 1a 1b 1c 4a 4b 4c 7a 7b 7c

W przypadku zagnieżdżania pętli każda pętla musi mieć unikatową element zmienną.

Można również zagnieżdżać różne rodzaje struktur sterujących w sobie. Aby uzyskać więcej informacji, zobacz Zagnieżdżone struktury kontrolek.

Zakończ działanie dla i kontynuuj

Instrukcja Exit For powoduje zakończenie Forwykonywania ...Next pętla i transferuje kontrolkę do instrukcji , która jest zgodna z instrukcją Next .

Instrukcja Continue For przenosi kontrolkę natychmiast do następnej iteracji pętli. Aby uzyskać więcej informacji, zobacz Continue, instrukcja.

W poniższym przykładzie pokazano, jak używać instrukcji Continue For and Exit For .

Dim numberSeq() As Integer =
    {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}

For Each number As Integer In numberSeq
    ' If number is between 5 and 8, continue
    ' with the next iteration.
    If number >= 5 And number <= 8 Then
        Continue For
    End If

    ' Display the number.
    Debug.Write(number.ToString & " ")

    ' If number is 10, exit the loop.
    If number = 10 Then
        Exit For
    End If
Next
Debug.WriteLine("")
' Output: 1 2 3 4 9 10

W pętli można umieścić dowolną For Each liczbę instrukcjiExit For. W przypadku użycia w pętlach Exit For zagnieżdżonych For Each wykonywanie powoduje wyjście z pętli najdalej wewnętrznej i transferuje kontrolkę do następnego wyższego poziomu zagnieżdżania.

Exit For jest często używany po ocenie jakiegoś warunku, na przykład w If...Then...Else Struktury. Może być konieczne użycie Exit For następujących warunków:

  • Kontynuowanie iteracji jest niepotrzebne lub niemożliwe. Może to być spowodowane błędną wartością lub żądaniem zakończenia.

  • Wyjątek jest przechwycony w ...TryCatch...Finally. Możesz użyć Exit For na końcu Finally bloku.

  • Istnieje nieskończona pętla, która jest pętlą, która może uruchamiać dużą lub nawet nieskończoną liczbę razy. Jeśli wykryjesz taki warunek, możesz użyć Exit For metody w celu ucieczki pętli. Aby uzyskać więcej informacji, zobacz Do... Loop, instrukcja.

Iteratory

Iterator służy do wykonywania niestandardowej iteracji w kolekcji. Iterator może być funkcją lub akcesorem Get . Używa instrukcji Yield , aby zwrócić każdy element kolekcji pojedynczo.

Iterator jest wywoływany przy użyciu instrukcji For Each...Next . Każda iteracja For Each pętli wywołuje iterator. Yield Po osiągnięciu instrukcji w iteratorze Yield wyrażenie w instrukcji jest zwracane, a bieżąca lokalizacja w kodzie jest zachowywana. Wykonanie jest uruchamiane ponownie z tej lokalizacji przy następnym wywołaniu iteratora.

W poniższym przykładzie użyto funkcji iteratora. Funkcja iteratora ma instrukcję Yield wewnątrz elementu For... Następna pętla. W metodzie ListEvenNumbers każda iteracja For Each treści instrukcji tworzy wywołanie funkcji iteratora, która przechodzi do następnej Yield instrukcji.

Public Sub ListEvenNumbers()
    For Each number As Integer In EvenSequence(5, 18)
        Debug.Write(number & " ")
    Next
    Debug.WriteLine("")
    ' Output: 6 8 10 12 14 16 18
End Sub

Private Iterator Function EvenSequence(
ByVal firstNumber As Integer, ByVal lastNumber As Integer) _
As System.Collections.Generic.IEnumerable(Of Integer)

    ' Yield even numbers in the range.
    For number = firstNumber To lastNumber
        If number Mod 2 = 0 Then
            Yield number
        End If
    Next
End Function

Aby uzyskać więcej informacji, zobacz Iteratory, Yield Statement i Iterator.

Realizacja techniczna

Gdy ...For EachNext instrukcja jest uruchamiana, program Visual Basic ocenia kolekcję tylko raz, zanim rozpocznie się pętla. Jeśli blok instrukcji zmieni się element lub group, te zmiany nie mają wpływu na iterację pętli.

Gdy wszystkie elementy w kolekcji zostały kolejno przypisane do elementelementu , pętla For Each zatrzymuje się i kontrolka przechodzi do instrukcji po instrukcji Next .

Jeśli wywnioskowanie opcji jest włączone (ustawienie domyślne), kompilator języka Visual Basic może wywnioskować typ elementdanych . Jeśli jest wyłączona i element nie została zadeklarowana poza pętlą, musisz zadeklarować ją w instrukcji For Each . Aby zadeklarować typ element danych jawnie, użyj klauzuli As . Chyba że typ danych elementu jest zdefiniowany poza konstrukcją For Each...Next , jego zakres jest treścią pętli. Należy pamiętać, że nie można zadeklarować element zarówno zewnętrznej, jak i wewnątrz pętli.

Opcjonalnie można określić element w instrukcji Next . Poprawia to czytelność programu, zwłaszcza jeśli masz zagnieżdżone For Each pętle. Musisz określić tę samą zmienną co zmienną wyświetlaną w odpowiedniej For Each instrukcji.

Możesz uniknąć zmiany wartości element wewnątrz pętli. Może to utrudnić odczytywanie i debugowanie kodu. Zmiana wartości group elementu nie ma wpływu na kolekcję ani jej elementy, które zostały określone podczas pierwszego wprowadzenia pętli.

W przypadku zagnieżdżania pętli, jeśli Next wystąpi instrukcja poziomu zagnieżdżania zewnętrznego przed Next poziomem wewnętrznym, kompilator sygnalizuje błąd. Jednak kompilator może wykryć ten nakładany błąd tylko wtedy, gdy zostanie określony element w każdej Next instrukcji.

Jeśli kod zależy od przechodzenia kolekcji w określonej kolejności, For Eachpętla ...Next nie jest najlepszym wyborem, chyba że znasz cechy obiektu wyliczającego, które uwidacznia kolekcja. Kolejność przechodzenia nie jest określana przez język Visual Basic, ale przez MoveNext metodę obiektu wyliczającego. W związku z tym może nie być w stanie przewidzieć, który element kolekcji jest pierwszym elementem, który ma zostać zwrócony w elementelemecie , lub który jest następny, który ma zostać zwrócony po danym elemecie. Możesz osiągnąć bardziej niezawodne wyniki przy użyciu innej struktury pętli, takiej jak For...Next lub Do...Loop.

Środowisko uruchomieniowe musi mieć możliwość przekonwertowania elementów w group pliku na element. Instrukcja [Option Strict] określa, czy dozwolone są konwersje rozszerzające i zawężające (Option Strict jest wyłączone, jego wartość domyślna) lub czy dozwolone są tylko konwersje rozszerzające (Option Strict jest włączone). Aby uzyskać więcej informacji, zobacz Zawężanie konwersji.

Typ group danych musi być typem odwołania odwołującym się do kolekcji lub tablicy, która jest wyliczana. Najczęściej oznacza to, że group odnosi się do obiektu, który implementuje IEnumerable interfejs System.Collections przestrzeni nazw lub IEnumerable<T> interfejs System.Collections.Generic przestrzeni nazw. System.Collections.IEnumerable definiuje metodę GetEnumerator , która zwraca obiekt modułu wyliczającego dla kolekcji. Obiekt modułu wyliczającego implementuje System.Collections.IEnumerator interfejs System.Collections przestrzeni nazw i uwidacznia Current właściwość oraz Reset metody i MoveNext . Program Visual Basic używa tych elementów do przechodzenia przez kolekcję.

Zawężanie konwersji

Gdy Option Strict jest ustawiona wartość On, konwersje zawężające zwykle powodują błędy kompilatora. For Each Jednak w instrukcji konwersje z elementów w group programie do element są oceniane i wykonywane w czasie wykonywania, a błędy kompilatora spowodowane zawężeniem konwersji są pomijane.

W poniższym przykładzie przypisanie m wartości jako początkowej wartości n nie jest kompilowane, gdy Option Strict jest włączone, ponieważ konwersja Long elementu na element Integer jest konwersją zawężającą. Jednak w instrukcji For Each nie jest zgłaszany żaden błąd kompilatora, mimo że przypisanie number wymaga tej samej konwersji z Long na Integer. W instrukcji For Each zawierającej dużą liczbę występuje błąd czasu wykonywania, gdy ToInteger jest stosowany do dużej liczby.

Option Strict On

Imports System

Module Program
    Sub Main(args As String())
        ' The assignment of m to n causes a compiler error when 
        ' Option Strict is on.
        Dim m As Long = 987
        'Dim n As Integer = m

        ' The For Each loop requires the same conversion but
        ' causes no errors, even when Option Strict is on.
        For Each number As Integer In New Long() {45, 3, 987}
            Console.Write(number & " ")
        Next
        Console.WriteLine()
        ' Output: 45 3 987

        ' Here a run-time error is raised because 9876543210
        ' is too large for type Integer.
        'For Each number As Integer In New Long() {45, 3, 9876543210}
        '    Console.Write(number & " ")
        'Next
    End Sub
End Module

Wywołania modułu IEnumerator

Po uruchomieniu For Eachpętli ...Next program Visual Basic sprawdza, czy group odwołuje się do prawidłowego obiektu kolekcji. Jeśli nie, zgłasza wyjątek. W przeciwnym razie wywołuje metodę MoveNext i Current właściwość obiektu modułu wyliczającego, aby zwrócić pierwszy element. Jeśli MoveNext wskazuje, że nie ma następnego elementu, oznacza to, że jeśli kolekcja jest pusta, For Each pętla zatrzymuje się i kontrolka przechodzi do instrukcji następującej po instrukcji Next . W przeciwnym razie program Visual Basic ustawia element element na pierwszy element i uruchamia blok instrukcji.

Za każdym razem, gdy program Visual Basic napotka instrukcję Next , powraca do instrukcji For Each . Ponownie wywołuje MoveNext i Current zwraca następny element, a następnie ponownie uruchamia blok lub zatrzymuje pętlę w zależności od wyniku. Ten proces będzie kontynuowany, dopóki MoveNext nie zostanie wyświetlony żaden następny element lub Exit For instrukcja nie zostanie napotkana.

Modyfikowanie kolekcji. Obiekt modułu wyliczającego zwracany przez GetEnumerator zwykle nie pozwala na zmianę kolekcji przez dodanie, usunięcie, zastąpienie lub zmianę kolejności wszystkich elementów. Jeśli zmienisz kolekcję po zainicjowaniu For Eachpętli ...Next , obiekt modułu wyliczającego stanie się nieprawidłowy, a następna próba uzyskania dostępu do elementu powoduje InvalidOperationException wyjątek.

Jednak to blokowanie modyfikacji nie jest określane przez visual basic, ale raczej przez implementację interfejsu IEnumerable . Istnieje możliwość zaimplementowania IEnumerable w sposób umożliwiający modyfikację podczas iteracji. Jeśli rozważasz taką dynamiczną modyfikację, upewnij się, że rozumiesz cechy IEnumerable implementacji używanej kolekcji.

Modyfikowanie elementów kolekcji. Właściwość Current obiektu modułu wyliczającego to ReadOnly i zwraca lokalną kopię każdego elementu kolekcji. Oznacza to, że nie można modyfikować samych elementów w For Eachpętli ...Next . Wszelkie wprowadzone modyfikacje mają wpływ tylko na lokalną kopię z Current i nie są odzwierciedlane z powrotem do bazowej kolekcji. Jeśli jednak element jest typem odwołania, można zmodyfikować elementy członkowskie wystąpienia, do którego wskazuje. Poniższy przykład modyfikuje BackColor element członkowski każdego thisControl elementu. Nie można jednak zmodyfikować thisControl samego siebie.

Sub LightBlueBackground(thisForm As System.Windows.Forms.Form)
    For Each thisControl In thisForm.Controls
        thisControl.BackColor = System.Drawing.Color.LightBlue
    Next thisControl
End Sub

Poprzedni przykład może modyfikować BackColor składowe każdego thisControl elementu, chociaż nie może modyfikować thisControl się.

Przechodzenie tablic. Array Ponieważ klasa implementuje IEnumerable interfejs, wszystkie tablice uwidaczniają metodę GetEnumerator . Oznacza to, że można iterować przez tablicę za pomocą For Eachpętli ...Next . Można jednak odczytywać tylko elementy tablicy. Nie można ich zmienić.

Przykład 1

W poniższym przykładzie wymieniono wszystkie foldery w katalogu C:\ przy użyciu DirectoryInfo klasy .

Dim dInfo As New System.IO.DirectoryInfo("c:\")
For Each dir As System.IO.DirectoryInfo In dInfo.GetDirectories()
    Debug.WriteLine(dir.Name)
Next

Przykład 2

Poniższy przykład ilustruje procedurę sortowania kolekcji. Przykład sortuje wystąpienia Car klasy przechowywanej w obiekcie List<T>. Klasa Car implementuje IComparable<T> interfejs, który wymaga CompareTo zaimplementowania metody.

Każde wywołanie CompareTo metody sprawia, że pojedyncze porównanie jest używane do sortowania. Kod napisany przez użytkownika w metodzie CompareTo zwraca wartość dla każdego porównania bieżącego obiektu z innym obiektem. Zwrócona wartość jest mniejsza niż zero, jeśli bieżący obiekt jest mniejszy niż inny obiekt, większy niż zero, jeśli bieżący obiekt jest większy niż inny obiekt i zero, jeśli są równe. Dzięki temu można zdefiniować w kodzie kryteria większe niż, mniejsze niż i równe.

W metodzie ListCarscars.Sort() instrukcja sortuje listę. To wywołanie Sort metody List<T> powoduje CompareTo , że metoda jest wywoływana automatycznie dla Car obiektów w obiekcie List.

Public Sub ListCars()

    ' Create some new cars.
    Dim cars As New List(Of Car) From
    {
        New Car With {.Name = "car1", .Color = "blue", .Speed = 20},
        New Car With {.Name = "car2", .Color = "red", .Speed = 50},
        New Car With {.Name = "car3", .Color = "green", .Speed = 10},
        New Car With {.Name = "car4", .Color = "blue", .Speed = 50},
        New Car With {.Name = "car5", .Color = "blue", .Speed = 30},
        New Car With {.Name = "car6", .Color = "red", .Speed = 60},
        New Car With {.Name = "car7", .Color = "green", .Speed = 50}
    }

    ' Sort the cars by color alphabetically, and then by speed
    ' in descending order.
    cars.Sort()

    ' View all of the cars.
    For Each thisCar As Car In cars
        Debug.Write(thisCar.Color.PadRight(5) & " ")
        Debug.Write(thisCar.Speed.ToString & " ")
        Debug.Write(thisCar.Name)
        Debug.WriteLine("")
    Next

    ' Output:
    '  blue  50 car4
    '  blue  30 car5
    '  blue  20 car1
    '  green 50 car7
    '  green 10 car3
    '  red   60 car6
    '  red   50 car2
End Sub

Public Class Car
    Implements IComparable(Of Car)

    Public Property Name As String
    Public Property Speed As Integer
    Public Property Color As String

    Public Function CompareTo(ByVal other As Car) As Integer _
        Implements System.IComparable(Of Car).CompareTo
        ' A call to this method makes a single comparison that is
        ' used for sorting.

        ' Determine the relative order of the objects being compared.
        ' Sort by color alphabetically, and then by speed in
        ' descending order.

        ' Compare the colors.
        Dim compare As Integer
        compare = String.Compare(Me.Color, other.Color, True)

        ' If the colors are the same, compare the speeds.
        If compare = 0 Then
            compare = Me.Speed.CompareTo(other.Speed)

            ' Use descending order for speed.
            compare = -compare
        End If

        Return compare
    End Function
End Class

Zobacz też