For Each...Next-Anweisung (Visual Basic)

Wiederholt eine Gruppe von Anweisungen für jedes Element in einer Sammlung.

Syntax

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

Bestandteile

Begriff Definition
element In der For Each-Anweisung erforderlich. In der Next-Anweisung optional. Variable Wird zum Durchlaufen der Elemente einer Sammlung verwendet.
datatype Optional, wenn Option Infer aktiviert (Standardeinstellung) oder element bereits deklariert ist; erforderlich, wenn Option Infer deaktiviert und element noch nicht deklariert ist. Der Datentyp von element.
group Erforderlich. Eine Variable vom Sammlungs- oder Objekttyp. Bezieht sich auf die Sammlung, für die statements wiederholt werden sollen.
statements Optional. Eine oder mehrere Anweisungen zwischen For Each und Next, die für jedes Element in groupausgeführt werden.
Continue For Optional. Überträgt die Steuerung an den Anfang der For Each-Schleife.
Exit For Optional. Überträgt die Steuerung aus dem For Each-Schleife.
Next Erforderlich. Beendet die Definition der For Each-Schleife.

Einfaches Beispiel

Verwenden Sie eine For Each...Next-Schleife, wenn Sie eine Reihe von Anweisungen für jedes Element einer Sammlung oder eines Arrays wiederholen möchten.

Tipp

Eine For...Next-Anweisung ist geeignet für Fälle, in denen Sie jede Schleifeniteration einer Steuervariable zuordnen können und die Anfangs- und Endwerte dieser Variable kennen. Bei einer Sammlung ist das Konzept von Anfangs- und Endwerten jedoch nicht sinnvoll, und möglicherweise kennen Sie auch nicht die Anzahl der Elemente in der Sammlung. In diesem Fall ist eine For Each...Next-Schleife oft die bessere Wahl.

Im folgenden Beispiel durchläuft die For Each...Next-Anweisung alle Elemente einer Listensammlung.

' 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

Weitere Beispiele finden Sie unter Sammlungen und Arrays.

Nested Loops

Sie können For Each-Schleifen schachteln, indem Sie eine Schleife in einer anderen platzieren.

Das folgende Beispiel veranschaulicht das Schachteln von For Each...Next-Strukturen.

' 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

Beim Schachteln von Schleifen muss jede Schleife über eine eindeutige element-Variable verfügen.

Sie können auch verschiedene Arten von Steuerungsstrukturen ineinander schachteln. Weitere Informationen finden Sie unter Geschachtelte Steuerungsstrukturen.

Exit For- und Continue For-Anweisung

Mit der Exit For-Anweisung können Sie die Ausführung der For...Next-Schleife beenden und die Steuerung an die Anweisung übertragen, die auf die Next-Anweisung folgt.

Die Continue For-Anweisung überträgt die Steuerung direkt an die nächste Iteration der Schleife. Weitere Informationen finden Sie unter Continue-Anweisung.

Im folgenden Beispiel wird veranschaulicht, wie Sie die Anweisungen Continue For und Exit For verwenden.

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

Eine For Each-Schleife kann beliebig viele Exit For-Anweisungen enthalten. Bei der Verwendung innerhalb von geschachtelten For Each-Schleifen beendet Exit For die Ausführung der innersten Schleife und überträgt die Steuerung an die nächsthöhere Ebene der Schachtelung.

Exit For wird oft nach der Auswertung einer Bedingung verwendet, z. B. in einer If...Then...Else-Struktur. Die Verwendung von Exit For eignet sich in den folgenden Fällen:

  • Eine Fortsetzung der Iteration ist unnötig oder nicht möglich. Der Grund kann ein fehlerhafter Wert oder eine Beendigungsanforderung sein.

  • In einer Try...Catch...Finally-Struktur wird eine Ausnahme abgefangen. Verwenden Sie Exit For am Ende des Finally-Blocks.

  • Es tritt eine Endlosschleife auf, d. h. eine Schleife, die sehr oft oder sogar unendlich oft ausgeführt werden kann. In einem solchen Fall können Sie mit Exit For die Schleife verlassen. Weitere Informationen finden Sie unter Do...Loop-Anweisung.

Iterators

Mit einem Iterator können Sie eine benutzerdefinierte Iteration für eine Sammlung ausführen. Ein Iterator kann eine Funktion oder ein Get-Accessor sein. Er verwendet eine Yield-Anweisung, um jedes Element der Sammlung einzeln zurückzugeben.

Sie rufen einen Iterator mithilfe einer For Each...Next-Anweisung auf. Jede Iteration der For Each-Schleife ruft den Iterator auf. Wird im Iterator eine Yield-Anweisung erreicht, wird der Ausdruck in der Yield-Anweisung zurückgegeben und die aktuelle Position im Code beibehalten. Wenn der Iterator das nächste Mal aufgerufen wird, wird die Ausführung von dieser Position neu gestartet.

Im folgenden Beispiel wird eine Iteratorfunktion verwendet. Die Iteratorfunktion verfügt über eine Yield-Anweisung, die sich in einer For…Next-Schleife befindet. In der ListEvenNumbers-Methode erstellt jede Iteration des For Each-Anweisungstexts einen Aufruf der Iteratorfunktion, die mit der nächsten Yield-Anweisung fortfährt.

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

Weitere Informationen finden Sie unter Iteratoren, Yield-Anweisung und Iterator.

Technische Implementierung

Bei der Ausführung einer For Each...Next-Anweisung wird die Sammlung von Visual Basic nur einmal ausgewertet, bevor die Schleife gestartet wird. Ändert der Anweisungsblock element oder group, ist die Iteration der Schleife davon nicht betroffen.

Wurden alle Elemente in der Sammlung nacheinander erfolgreich element zugewiesen, wird die For Each-Schleife beendet und die Steuerung an die Anweisung übergeben, die auf die Next-Anweisung folgt.

Ist Option Infer aktiviert (Standardeinstellung), kann der Visual Basic-Compiler den Datentyp von element ableiten. Ist die Anweisung deaktiviert und wurde element nicht außerhalb der Schleife deklariert, müssen Sie dies in der For Each-Anweisung tun. Verwenden Sie eine As-Klausel, um den Datentyp von element explizit zu deklarieren. Ist der Datentyp von „element“ innerhalb des For Each...Next-Konstrukts definiert, stellt dessen Bereich den Inhalt der Schleife dar. Beachten Sie, dass Sie element nicht sowohl außerhalb als auch innerhalb der Schleife deklarieren können.

Optional können Sie element in der Next-Anweisung angeben. Dadurch wird die Lesbarkeit Ihres Programms verbessert, insbesondere bei geschachtelten For Each-Schleifen. Sie müssen die gleiche Variable angeben, die in der entsprechenden For Each-Anweisung angezeigt wird.

Sie sollten vermeiden, den Wert von element innerhalb einer Schleife zu ändern. Andernfalls kann das Lesen und Debuggen Ihres Codes erschwert werden. Ein geänderter Wert von group hat keine Auswirkungen auf die Sammlung oder die Sammlungselemente, die zu Beginn der Schleife bestimmt wurden.

Tritt beim Schachteln von Schleifen eine Next-Anweisung einer äußeren Schachtelungsebene vor der Next-Anweisung einer inneren Ebene auf, signalisiert der Compiler einen Fehler. Der Compiler kann diesen überlappenden Fehler jedoch nur erkennen, wenn Sie element in jeder Next-Anweisung angeben.

Folgt Ihr Code beim Durchlaufen einer Sammlung einer bestimmten Reihenfolge, ist eine For Each...Next-Schleife nicht die beste Option, sofern Sie die Merkmale des Enumeratorobjekts, das die Sammlung verfügbar macht, nicht kennen. Die Reihenfolge des Durchlaufs wird nicht von Visual Basic bestimmt, sondern von der MoveNext-Methode des Enumeratorobjekts. Daher können Sie möglicherweise nicht vorhersagen, welches Element der Sammlung als Erstes in element zurückgegeben wird, oder welches Element als Nächstes nach einem bestimmten Element zurückgegeben wird. Mit einer anderen Schleifenstruktur (z. B. For...Next oder Do...Loop) können Sie möglicherweise zuverlässigere Ergebnisse erzielen.

Die Runtime muss in der Lage sein, die Elemente von group in element zu konvertieren. Mit der [Option Strict]-Anweisung wird gesteuert, ob sowohl erweiternde als auch einschränkende Konvertierungen zulässig sind (wenn Option Strict deaktiviert ist, Standardeinstellung), oder ob nur erweiternde Konvertierungen zulässig sind (wenn Option Strict aktiviert ist). Weitere Informationen finden Sie unter Einschränkende Konvertierungen.

Der Datentyp von group muss ein Verweistyp sein, der auf eine aufzählbare Sammlung oder ein aufzählbares Array verweist. In der Regel bedeutet dies, dass group auf ein Objekt verweist, das die IEnumerable-Schnittstelle des System.Collections-Namespaces oder die IEnumerable<T>-Schnittstelle des System.Collections.Generic-Namespaces implementiert. System.Collections.IEnumerable definiert die GetEnumerator-Methode, die ein Enumeratorobjekt für die Sammlung zurückgibt. Das Enumeratorobjekt implementiert die System.Collections.IEnumerator-Schnittstelle des System.Collections-Namespaces und macht die Current-Eigenschaft und die Methoden Reset und MoveNext verfügbar. Visual Basic verwendet diese Methoden zum Durchlaufen der Sammlung.

Eingrenzungskonvertierungen

Ist Option Strict auf On festgelegt, verursachen einschränkende Konvertierungen normalerweise Compilerfehler. In einer For Each-Anweisung werden Konvertierungen der Elemente von group in element jedoch zur Laufzeit ausgewertet und ausgeführt und Compilerfehler, die durch einschränkende Konvertierungen ausgelöst werden, unterdrückt.

Im folgenden Beispiel wird die Zuweisung von m als Anfangswert für n nicht kompiliert, wenn Option Strict aktiviert ist, da die Konvertierung von Long in Integer eine einschränkende Konvertierung darstellt. In der For Each-Anweisung wird jedoch kein Compilerfehler gemeldet, obwohl die Zuweisung zu number die gleiche Konvertierung von Long in Integer erfordert. In der For Each-Anweisung, die eine große Zahl enthält, tritt ein Laufzeitfehler auf, wenn ToInteger auf die große Zahl angewendet wird.

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

IEnumerator-Aufrufe

Zu Beginn der Ausführung einer For Each...Next-Schleife überprüft Visual Basic, ob group auf ein gültiges Sammlungsobjekt verweist. Ist dies nicht der Fall, wird eine Ausnahme ausgelöst. Andernfalls werden die MoveNext-Methode und die Current-Eigenschaft des Enumeratorobjekts zur Rückgabe des ersten Elements aufgerufen. Gibt MoveNext an, dass kein nächstes Element vorhanden ist (die Sammlung also leer ist), wird die For Each-Schleife beendet und die Steuerung an die Anweisung übergeben, die auf die Next-Anweisung folgt. Andernfalls legt Visual Basic element auf das erste Element fest und führt den Anweisungsblock aus.

Sobald Visual Basic auf die Next-Anweisung trifft, kehrt es wieder zur For Each-Anweisung zurück. Erneut erfolgt der Aufruf von MoveNext und Current zur Rückgabe des nächsten Elements, und je nach Ergebnis wird entweder der Block ausgeführt oder die Schleife beendet. Dieser Prozess wird fortgesetzt, bis MoveNext anzeigt, dass kein nächstes Element vorhanden ist oder eine Exit For-Anweisung gefunden wurde.

Ändern der Sammlung: Mit dem von GetEnumerator zurückgegebenen Enumeratorobjekt können Sie die Sammlung normalerweise nicht ändern, indem Sie Elemente hinzufügen, löschen, ersetzen oder neu anordnen. Wenn Sie die Sammlung nach der Initiierung einer For Each...Next-Schleife ändern, wird das Enumeratorobjekt ungültig, und der nächste Zugriffsversuch auf ein Element verursacht eine InvalidOperationException-Ausnahme.

Dieses Blockieren von Änderungen wird jedoch nicht von Visual Basic bestimmt, sondern von der Implementierung der IEnumerable-Schnittstelle. Sie können IEnumerable so implementieren, dass Änderungen während der Iteration möglich sind. Wenn Sie über diese Art der dynamischen Änderung nachdenken, sollten Sie die Merkmale der IEnumerable-Implementierung für die von Ihnen verwendete Sammlung genau kennen.

Ändern von Sammlungselementen: Die Current-Eigenschaft des Enumeratorobjekts ist ReadOnly und gibt eine lokale Kopie aller Sammlungselemente zurück. Dies bedeutet, dass Sie in einer For Each...Next-Schleife die Elemente selbst nicht ändern können. Jede Änderung wirkt sich nur auf die lokale Kopie von Current aus und wird in der zugrunde liegenden Sammlung nicht wiedergegeben. Handelt es sich bei einem Element jedoch um einen Verweistyp, können Sie die Member der Instanz ändern, auf die es verweist. Im folgenden Beispiel wird der BackColor-Member aller thisControl-Elemente geändert. thisControl selbst kann jedoch nicht geändert werden.

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

Im vorherigen Beispiel wird der BackColor-Member aller thisControl-Elemente geändert, jedoch nicht thisControl selbst.

Durchlaufen von Arrays: Da die Array-Klasse die IEnumerable-Schnittstelle implementiert, machen alle Arrays die GetEnumerator-Methode verfügbar. Dies bedeutet, dass Sie ein Array mit einer For Each...Next-Schleife durchlaufen können. Sie können die Arrayelemente jedoch nur lesen. Sie können sie nicht ändern.

Beispiel 1

Im folgenden Beispiel werden mithilfe der DirectoryInfo-Klasse alle Ordner im Verzeichnis „C:\“ aufgelistet.

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

Beispiel 2

Das folgende Beispiel zeigt ein Verfahren zum Sortieren einer Auflistung. In dem Beispiel werden Instanzen einer Car-Klasse sortiert, die in List<T> gespeichert sind. Die Car-Klasse implementiert die IComparable<T>-Schnittstelle, die die Implementierung der CompareTo-Methode erfordert.

Jeder Aufruf der CompareTo-Methode führt einen einzelnen Vergleich aus, der für die Sortierung verwendet wird. Vom Benutzer erstellter Code in der CompareTo-Methode gibt einen Wert für jeden Vergleich des aktuellen Objekts mit einem anderen Objekt zurück. Der zurückgegebene Wert ist kleiner als Null, wenn das aktuelle Objekt kleiner ist als das andere Objekt, größer als Null, wenn das aktuelle Objekt größer als das andere Objekt ist und Null, wenn beide Objekt gleich groß sind. Dies ermöglicht es Ihnen, in dem Code die Kriterien für größer als, kleiner als und gleich zu definieren.

In der ListCars-Methode sortiert die cars.Sort()-Anweisung die Liste. Dieser Aufruf der Sort-Methode von List<T> führt dazu, dass die CompareTo-Methode für die Car-Objekte in der List automatisch aufgerufen wird.

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

Siehe auch