Istruzione For Each...Next (Visual Basic)

Ripete un gruppo di istruzioni per ogni elemento di una raccolta.

Sintassi

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

Parti

Termine Definizione
element Obbligatorio nell'istruzione For Each . Facoltativo nell'istruzione Next . Variabile. Utilizzato per scorrere gli elementi della raccolta.
datatype Facoltativo se Option Infer è attivato (impostazione predefinita) o element è già dichiarato; obbligatorio se Option Infer è disattivato e element non è già dichiarato. Tipo di dati del parametro element.
group Obbligatorio. Variabile con un tipo che è un tipo di raccolta o Object. Fa riferimento alla raccolta su cui deve essere ripetuto l'oggetto statements .
statements Facoltativa. Una o più istruzioni tra For Each e Next eseguite su ogni elemento in group.
Continue For Facoltativa. Trasferisce il controllo all'inizio del For Each ciclo.
Exit For Facoltativa. Trasferisce il controllo fuori dal For Each ciclo.
Next Obbligatorio. Termina la definizione del For Each ciclo.

Esempio semplice

Usare un For Eachciclo ...Next quando si desidera ripetere un set di istruzioni per ogni elemento di una raccolta o di una matrice.

Suggerimento

Un for... Next Statement funziona bene quando è possibile associare ogni iterazione di un ciclo a una variabile di controllo e determinare i valori iniziali e finali della variabile. Tuttavia, quando si usa una raccolta, il concetto di valori iniziali e finali non è significativo e non si conosce necessariamente il numero di elementi presenti nella raccolta. In questo tipo di caso, un For Eachciclo ...Next è spesso una scelta migliore.

Nell'esempio seguente, ...For EachNext l'istruzione scorre tutti gli elementi di un insieme 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

Per altri esempi, vedere Raccolte e matrici.

Nested Loops

È possibile annidare For Each i cicli inserendo un ciclo all'interno di un altro.

Nell'esempio seguente viene illustrato l'annidamento For Each...Next Strutture.

' 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

Quando si annidano cicli, ogni ciclo deve avere una variabile univoca element .

È anche possibile annidare diversi tipi di strutture di controllo tra loro. Per altre informazioni, vedere Strutture di controllo annidate.

Esci per e continua per

L'istruzione Exit For causa l'uscita dall'istruzione For...Next loop e trasferisce il controllo all'istruzione che segue l'istruzione Next .

L'istruzione Continue For trasferisce immediatamente il controllo all'iterazione successiva del ciclo. Per altre informazioni, vedere Istruzione Continue.

Nell'esempio seguente viene illustrato come usare le Continue For istruzioni e 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

È possibile inserire un numero qualsiasi di Exit For istruzioni in un For Each ciclo. Se usato all'interno di cicli annidati For Each , Exit For causa l'uscita dall'esecuzione del ciclo più interno e trasferisce il controllo al livello superiore successivo di annidamento.

Exit For viene spesso usato dopo una valutazione di alcune condizioni, ad esempio in un If...Then ...Else Struttura. È possibile usare Exit For per le condizioni seguenti:

  • Continuare a eseguire l'iterazione non è necessario o impossibile. Ciò potrebbe essere causato da un valore errato o da una richiesta di terminazione.

  • Un'eccezione viene intercettata in un Try...Catch ...Finally. È possibile usare Exit For alla fine del Finally blocco.

  • Esiste un ciclo infinito, ovvero un ciclo che può essere eseguito un numero elevato o persino infinito di volte. Se si rileva una condizione di questo tipo, è possibile usare per eseguire Exit For l'escape del ciclo. Per altre informazioni, vedere Do... Istruzione Loop.

Iterators

Si usa un iteratore per eseguire un'iterazione personalizzata su una raccolta. Un iteratore può essere una funzione o una Get funzione di accesso. Usa un'istruzione Yield per restituire ogni elemento della raccolta uno alla volta.

Per chiamare un iteratore, usare un'istruzione For Each...Next . Ogni iterazione del ciclo For Each chiama l'iteratore. Quando viene raggiunta un'istruzione Yield nell'iteratore, viene restituita l'espressione nell'istruzione Yield e viene mantenuta la posizione corrente nel codice. L'esecuzione viene ripresa a partire da quella posizione la volta successiva che viene chiamato l'iteratore.

Nell'esempio seguente viene usata una funzione iteratore. La funzione iteratore ha un'istruzione Yield che si trova all'interno di un for... Ciclo successivo . ListEvenNumbers Nel metodo ogni iterazione del corpo dell'istruzione For Each crea una chiamata alla funzione iteratore, che procede all'istruzione successivaYield.

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

Per altre informazioni, vedere Iteratori, istruzione Yield e Iterator.

Implementazione tecnica

Quando un For Each...Next l'istruzione viene eseguita, Visual Basic valuta la raccolta una sola volta, prima dell'avvio del ciclo. Se l'istruzione blocca le modifiche element o group, queste modifiche non influiscono sull'iterazione del ciclo.

Quando tutti gli elementi della raccolta sono stati assegnati successivamente a element, il For Each ciclo si arresta e il controllo passa all'istruzione che segue l'istruzione Next .

Se Option Infer è attivato (impostazione predefinita), il compilatore Visual Basic può dedurre il tipo di dati di element. Se è disattivato e element non è stato dichiarato all'esterno del ciclo, è necessario dichiararlo nell'istruzione For Each . Per dichiarare il tipo di dati in element modo esplicito, usare una As clausola . A meno che il tipo di dati dell'elemento non sia definito al di fuori del For Eachcostrutto ...Next , l'ambito è il corpo del ciclo. Si noti che non è possibile dichiarare element sia all'esterno che all'interno del ciclo.

Facoltativamente, è possibile specificare element nell'istruzione Next . Ciò migliora la leggibilità del programma, soprattutto se sono presenti cicli annidati For Each . È necessario specificare la stessa variabile di quella visualizzata nell'istruzione corrispondente For Each .

È possibile evitare di modificare il valore di all'interno di element un ciclo. Questa operazione può rendere più difficile leggere ed eseguire il debug del codice. La modifica del valore di group non influisce sulla raccolta o sui relativi elementi, che sono stati determinati quando il ciclo è stato immesso per la prima volta.

Quando si annida cicli, se viene rilevata un'istruzione Next di un livello di annidamento esterno prima Next di di un livello interno, il compilatore segnala un errore. Tuttavia, il compilatore può rilevare questo errore sovrapposto solo se si specifica element in ogni Next istruzione.

Se il codice dipende dall'attraversamento di una raccolta in un ordine specifico, un For Eachciclo ...Next non è la scelta migliore, a meno che non si conoscano le caratteristiche dell'oggetto enumeratore esposto dall'insieme. L'ordine di attraversamento non è determinato da Visual Basic, ma dal MoveNext metodo dell'oggetto enumeratore. Pertanto, potrebbe non essere possibile stimare quale elemento della raccolta è il primo a essere restituito in elemento che è l'elemento successivo a essere restituito dopo un determinato elemento. È possibile ottenere risultati più affidabili usando una struttura di ciclo diversa, ad esempio For...Next o Do...Loop.

Il runtime deve essere in grado di convertire gli elementi in groupelement. L'istruzione [Option Strict] controlla se sono consentite conversioni sia di tipo widening che di tipo narrowing (Option Strict è disattivato, il relativo valore predefinito) o se sono consentite solo conversioni di tipo widening (Option Strict è attivato). Per altre informazioni, vedere Conversioni di tipo narrowing.

Il tipo di dati di group deve essere un tipo riferimento che fa riferimento a una raccolta o a una matrice che è enumerabile. In genere ciò significa che group fa riferimento a un oggetto che implementa l'interfaccia IEnumerable dello System.Collections spazio dei nomi o l'interfaccia IEnumerable<T> dello System.Collections.Generic spazio dei nomi. System.Collections.IEnumerable definisce il GetEnumerator metodo , che restituisce un oggetto enumeratore per la raccolta. L'oggetto enumeratore implementa l'interfaccia System.Collections.IEnumerator dello System.Collections spazio dei nomi ed espone la Current proprietà e i Reset metodi e MoveNext . Visual Basic usa questi oggetti per attraversare la raccolta.

conversioni verso un tipo di dati più piccolo

Quando Option Strict è impostato su On, le conversioni di restringezione causano normalmente errori del compilatore. In un'istruzioneFor Each, tuttavia, le conversioni degli elementi in in elementgroup vengono valutate ed eseguite in fase di esecuzione e gli errori del compilatore causati da conversioni limitate vengono eliminati.

Nell'esempio seguente l'assegnazione di m come valore iniziale per n non viene compilata quando Option Strict è attiva perché la conversione di un oggetto in un LongInteger oggetto è una conversione ridotta. Nell'istruzione, tuttavia, non viene segnalato alcun errore del For Each compilatore, anche se l'assegnazione per number richiedere la stessa conversione da Long a Integer. Nell'istruzione For Each contenente un numero elevato si verifica un errore di runtime quando ToInteger viene applicato al numero elevato.

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

Chiamate di IEnumerator

Quando l'esecuzione di un ciclo ...Next viene avviata, Visual Basic verifica che group si riferisce a un For Eachoggetto di raccolta valido. In caso contrario, genera un'eccezione. In caso contrario, chiama il metodo e la MoveNextCurrent proprietà dell'oggetto enumeratore per restituire il primo elemento. Se MoveNext indica che non esiste alcun elemento successivo, ovvero se la raccolta è vuota, il For Each ciclo si arresta e il controllo passa all'istruzione che segue l'istruzione Next . In caso contrario, Visual Basic imposta element sul primo elemento ed esegue il blocco dell'istruzione.

Ogni volta che Visual Basic rileva l'istruzione, viene restituita all'istruzione NextFor Each . MoveNext Chiama di nuovo e Current restituisce l'elemento successivo e di nuovo esegue il blocco o arresta il ciclo a seconda del risultato. Questo processo continua fino a quando MoveNext non è presente alcun elemento successivo o viene rilevata un'istruzione Exit For .

Modifica della raccolta. L'oggetto enumeratore restituito normalmente GetEnumerator non consente di modificare la raccolta aggiungendo, eliminando, sostituendo o riordinando eventuali elementi. Se si modifica la raccolta dopo aver avviato un For Eachciclo ...Next , l'oggetto enumeratore diventa non valido e il successivo tentativo di accesso a un elemento causa un'eccezione InvalidOperationException .

Tuttavia, questo blocco di modifiche non è determinato da Visual Basic, ma piuttosto dall'implementazione dell'interfaccia IEnumerable . È possibile implementare IEnumerable in modo da consentire la modifica durante l'iterazione. Se si sta valutando questa modifica dinamica, assicurarsi di comprendere le caratteristiche dell'implementazione IEnumerable nella raccolta in uso.

Modifica degli elementi della raccolta. La Current proprietà dell'oggetto enumeratore è ReadOnly e restituisce una copia locale di ogni elemento della raccolta. Ciò significa che non è possibile modificare gli elementi stessi in un For Eachciclo ...Next . Tutte le modifiche apportate influiscono solo sulla copia locale da Current e non vengono riflesse nuovamente nella raccolta sottostante. Tuttavia, se un elemento è un tipo di riferimento, è possibile modificare i membri dell'istanza in cui punta. Nell'esempio seguente viene modificato il BackColor membro di ogni thisControl elemento. Non è tuttavia possibile modificare thisControl se stesso.

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

L'esempio precedente può modificare il BackColor membro di ogni thisControl elemento, anche se non può modificarlo thisControl .

Attraversamento di matrici. Poiché la Array classe implementa l'interfaccia IEnumerable , tutte le matrici espongono il GetEnumerator metodo. Ciò significa che è possibile scorrere una matrice con un For Eachciclo ...Next . Tuttavia, è possibile leggere solo gli elementi della matrice. Non è possibile modificarli.

Esempio 1

Nell'esempio seguente vengono elencate tutte le cartelle in C:\ directory usando la DirectoryInfo classe .

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

Esempio 2

L'esempio seguente illustra una procedura per ordinare una raccolta. L'esempio ordina istanze di una Car classe archiviata in un List<T>oggetto . La classe Car implementa l'interfaccia IComparable<T>, che richiede l'implementazione del metodo CompareTo.

Ogni chiamata al CompareTo metodo esegue un singolo confronto usato per l'ordinamento. Il codice scritto dall'utente presente nel metodo CompareTo restituisce un valore per ogni confronto dell'oggetto corrente con un altro oggetto. Il valore restituito è minore di zero se l'oggetto corrente è inferiore all'altro oggetto, maggiore di zero se l'oggetto corrente è superiore all'altro oggetto e zero se sono uguali. In questo modo è possibile definire nel codice i criteri di maggiore, minore e uguale.

Nel metodo ListCars l'istruzione cars.Sort() ordina l'elenco. Questa chiamata al metodo Sort di List<T> determina la chiamata automatica al metodo CompareTo per gli oggetti Car in 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

Vedi anche