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 è attivo (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 Facoltativo. Una o più istruzioni tra For Each e Next eseguite su ogni elemento in group.
Continue For Facoltativo. Trasferisce il controllo all'inizio del ciclo For Each.
Exit For Facoltativo. Trasferisce il controllo fuori dal ciclo For Each.
Next Obbligatorio. Termina la definizione del ciclo For Each.

Esempio semplice

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

Suggerimento

Un’Istruzione...Next 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 ha a che fare con una raccolta, il concetto di valori iniziali e finali non è significativo e non si conosce necessariamente il numero di elementi della raccolta. In questo tipo di caso, un ciclo For Each...Next è spesso una scelta migliore.

Nell'esempio seguente, For Each…L’istruzione Next scorre tutti gli elementi di una raccolta 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 i cicli For Each inserendone uno all'interno di un altro.

L'esempio seguente illustra i For Eachannidati…Strutture Next.

' 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 Esci per causa l'uscita da ForNext esegue il 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 istruzioni Continue For 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 istruzioni Exit For in un ciclo For Each. Se usato all'interno di cicli For Each annidati, Exit For l'esecuzione esce dal 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 una struttura If...Then...Else. È 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 rilevata in un Try...Catch...Finally. È possibile usare Exit For alla fine del blocco di Finally.

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

Iteratori

Si usa un iteratore per eseguire un'iterazione personalizzata su una raccolta. Un iteratore può essere una funzione o una funzione di accesso Get. 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 utilizzata una funzione iteratore. La funzione iteratore ha un'istruzione Yield all'interno di un ciclo For…Next. Nel metodo ListEvenNumbers ogni iterazione del corpo dell'istruzione For Each crea una chiamata alla funzione iteratore, che procede con l'istruzione Yield successiva.

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

Implementazione tecnica

Quando un For Each…L’istruzione Next 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 ciclo For Each si arresta e il controllo passa all'istruzione che segue l'istruzione Next.

Se Option Infer è attivato (impostazione predefinita), il compilatore di 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 di element in modo esplicito, usare una clausola As. A meno che il tipo di dati dell'elemento non sia definito al di fuori del costrutto For Each...Next, il relativo 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 For Each annidati. È necessario specificare la stessa variabile di quella visualizzata nell'istruzione For Each corrispondente.

È possibile evitare di modificare il valore di element all'interno di 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 annidano cicli, se viene rilevata un'istruzione Next di un livello di annidamento esterno prima di Next di un livello interno, il compilatore segnala un errore. Tuttavia, il compilatore può rilevare questo errore sovrapposto solo se si specifica element in ogni istruzione Next.

Se il codice dipende dall'attraversamento di una raccolta in un determinato ordine, un ciclo For Each...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 metodo MoveNext dell'oggetto enumeratore. Pertanto, potrebbe non essere possibile stimare quale elemento della raccolta è il primo a essere restituito in element, o che è il 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 group in element. L'istruzione [Option Strict] controlla se sono consentite conversioni di tipo più esteso e di tipo narrow (Option Strict è disattivato, il suo valore predefinito) o se sono consentite solo conversioni verso un tipo più esteso (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 enumerabile. In genere questo significa che group fa riferimento a un oggetto che implementa l'interfaccia IEnumerable dello spazio dei nomi System.Collections o l'interfaccia IEnumerable<T> dello spazio dei nomi System.Collections.Generic. System.Collections.IEnumerable definisce il metodo GetEnumerator, che restituisce un oggetto enumeratore per l'insieme. L'oggetto enumeratore implementa l'interfaccia System.Collections.IEnumerator dello spazio dei nomi System.Collections ed espone la proprietà Current e i metodi Reset 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 tipo narrow causano in genere errori del compilatore. In un'istruzione For Each, tuttavia, le conversioni dagli elementi in in elementgroup vengono valutate ed eseguite in fase di esecuzione e gli errori del compilatore causati da conversioni di tipo narrowing vengono eliminati.

Nell'esempio seguente, l'assegnazione di m come valore iniziale per n non viene compilato quando Option Strict è attivato perché la conversione di un oggetto Long in Integer è una conversione che supporta un minor numero di dati. Nell'istruzione For Each, tuttavia, non viene segnalato alcun errore del compilatore, anche se l'assegnazione a number richiede la stessa conversione da Long a Integer. Nell'istruzione For Each che contiene un numero elevato, si verifica un errore di run-time 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 A IEnumerator

Quando viene avviata l'esecuzione di un ciclo For Each...Next, Visual Basic verifica che group faccia riferimento a un oggetto raccolta valido. In caso contrario, genera un'eccezione. In caso contrario, chiama il metodo MoveNext e la proprietà Current dell'oggetto enumeratore per restituire il primo elemento. Se MoveNext indica che non è presente alcun elemento successivo, ovvero se la raccolta è vuota, il ciclo For Each 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 di istruzioni.

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

Modifica della raccolta. L'oggetto enumeratore restituito da GetEnumerator normalmente non consente di modificare la raccolta aggiungendo, eliminando, sostituendo o riordinando gli elementi. Se si modifica la raccolta dopo aver avviato un ciclo For Each...Next, l'oggetto enumeratore diventa non valido e il tentativo successivo di accedere 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 che consenta la modifica durante l'iterazione. Se si sta valutando l'esecuzione di questa modifica dinamica, assicurarsi di comprendere le caratteristiche dell'implementazione IEnumerable nella raccolta in uso.

Modifica degli elementi della raccolta. La proprietà Current 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 ciclo For Each...Next. Qualsiasi modifica apportata influisce solo sulla copia locale da Current e non viene riflessa nuovamente nella raccolta sottostante. Tuttavia, se un elemento è un tipo riferimento, è possibile modificare i membri dell'istanza in cui punta. Nell'esempio seguente viene modificato il membro BackColor di ogni elemento thisControl. Non è tuttavia possibile modificare lo stesso thisControl.

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 membro BackColor di ogni elemento thisControl, anche se non può modificare lo stesso thisControl.

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

Esempio 1

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

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. Nell'esempio vengono ordinate istanze di una classe Car archiviate in un List<T>. La classe Car implementa l'interfaccia IComparable<T>, che richiede l'implementazione del metodo CompareTo.

Ogni chiamata al metodo CompareTo effettua un confronto unico che viene 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