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 ]

Części

Okres 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 Wymagane. 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 Each i Next uruchamiana na każdym elemencie w elemencie group.
Continue For Opcjonalny. Transferuje kontrolkę do początku For Each pętli.
Exit For Opcjonalny. Transferuje kontrolę z For Each pętli.
Next Wymagane. Kończy 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.

Porada

A dla... Następna instrukcja działa 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 niekoniecznie wiesz, ile elementów zawiera 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 Listy.

' 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.

Zagnieżdżone pętle

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

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 kontrolek w sobie. Aby uzyskać więcej informacji, zobacz Zagnieżdżone struktury kontrolek.

Zakończ dla i kontynuuj dla

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 Kontynuuj instrukcję.

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 zagnieżdżonych For Each pętlach Exit For wykonywanie powoduje zamknięcie najbardziej wewnętrznej pętli i transferowanie kontrolki do następnego wyższego poziomu zagnieżdżania.

Exit For jest często używany po ocenie pewnego 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 Try...Catch ...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 polecenia , aby uciec od 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 wyrażenie w instrukcji jest zwracane, a bieżąca lokalizacja w Yield 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 zawiera instrukcję Yield , która znajduje się 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

Kiedy ...For EachNextinstrukcje są uruchamiane, Visual Basic ocenia kolekcję tylko raz przed uruchomieniem pętli. Jeśli blok instrukcji zmieni się element lub group, te zmiany nie wpływają na iterację pętli.

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

Jeśli opcja Wnioskowanie jest włączone (jego ustawienie domyślne), kompilator Visual Basic może wywnioskować typ danych .element 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 For Eachkonstrukcją ...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. Należy 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. Wykonanie tej czynności może 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 zostanie napotkana instrukcja poziomu zagnieżdżania zewnętrznego przed Next poziomem wewnętrznym, kompilator sygnalizuje błąd. Jednak kompilator może wykryć ten nakładające się błędy tylko wtedy, gdy określisz 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 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ępnym elementem zwracanym 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 być w stanie przekonwertować elementy na groupelement. 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 danych musi być typem group referencyjnym, który odwołuje się do kolekcji lub tablicy, która jest wyliczalna. Najczęściej oznacza to, że group odwołuje 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 wyliczający dla kolekcji. Obiekt wyliczający implementuje System.Collections.IEnumerator interfejs System.Collections przestrzeni nazw i uwidacznia Current właściwość oraz metody i Reset .MoveNext Visual Basic używa ich do przechodzenia przez kolekcję.

Zawężanie konwersji

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

W poniższym przykładzie przypisanie wartości początkowej mn dla elementu nie jest kompilowane, Option Strict ponieważ konwersja elementu na element LongInteger jest konwersją zawężającą. Jednak w instrukcji For Each nie jest zgłaszany żaden błąd kompilatora, mimo że przypisanie wymaga number 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 Visual Basic sprawdza, czy group odwołuje się do prawidłowego obiektu kolekcji. Jeśli tak nie jest, zgłasza wyjątek. W przeciwnym razie wywołuje metodę MoveNext i Current właściwość obiektu 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 po Next instrukcji . W przeciwnym razie Visual Basic ustawia element element na pierwszy element i uruchamia blok instrukcji.

Za każdym razem, gdy Visual Basic napotka instrukcjęNext, zwraca ją 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 ani Exit For instrukcja.

Modyfikowanie kolekcji. Obiekt modułu wyliczającego zwrócony 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 przez implementację interfejsuIEnumerable. Istnieje możliwość zaimplementowania IEnumerable w sposób umożliwiający modyfikację podczas iteracji. Jeśli rozważasz wykonanie takiej dynamicznej modyfikacji, 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 wpływają 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 modyfikować thisControl się.

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 zmodyfikować BackColor element członkowski każdego thisControl elementu, chociaż nie może się modyfikować thisControl .

Przechodzenie tablic. Array Ponieważ klasa implementuje IEnumerable interfejs, wszystkie tablice uwidaczniają metodęGetEnumerator. Oznacza to, że można iterować 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 folderze C:\ katalog 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

W poniższym przykładzie przedstawiono 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 powoduje pojedyncze porównanie 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ż