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 Each Next 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 Each
Uż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 Each
Next
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 For
wykonywania ...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 ...
Try
Catch
...Finally
. Możesz użyćExit For
na końcuFinally
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 Each
Next
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 element
elementu , 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 element
danych . 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 Each
pę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 element
elemecie , 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 Each
pę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 Each
pę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 Each
pę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 Each
pę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 ListCars
cars.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