For Each...Next, instruction (Visual Basic)

Répète un groupe d’instructions pour chaque élément d’une collection.

Syntaxe

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

Éléments

Terme Définition
element Obligatoire dans l’instruction For Each. Facultatif dans l’instruction Next. Variable. Utilisé pour l’itération des éléments de la collection.
datatype Facultatif si Option Infer est activée (elle l’est par défaut) ou element déjà déclaré ; obligatoire si Option Infer est désactivée et element n’est pas déjà déclaré. Le type de données element.
group Obligatoire. Une variable avec un type qui est un type de collection ou Objet. Fait référence à la collection sur laquelle les statements doivent être répétées.
statements Optionnel. Une ou plusieurs instructions entre For Each et Next qui s’exécutent sur chaque élément dans group.
Continue For Optionnel. Transfère le contrôle au début de la boucle For Each.
Exit For Optionnel. Transfère le contrôle hors de la boucle For Each.
Next Obligatoire. Met fin à la définition de la boucle For Each.

Exemple simple

Utilisez une boucle For Each...Next lorsque vous souhaitez répéter un ensemble d’instructions pour chaque élément d’une collection ou d’un tableau.

Conseil

Une instruction For...Next fonctionne bien lorsque vous pouvez associer chaque itération d’une boucle à une variable de contrôle et déterminer les valeurs initiales et finales de cette variable. Toutefois, lorsque vous avez affaire à une collection, le concept de valeurs initiales et finales n’est pas significatif et vous ne savez pas nécessairement combien d’éléments la collection a. Dans ce genre de cas, une boucle For Each...Next constitue souvent un meilleur choix.

Dans l’exemple suivant, l’instruction For EachNext itère dans tous les éléments d’une collection de listes.

' 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

Pour plus d’exemples, consultez Collections et Tableaux.

Nested Loops

Vous pouvez imbriquer des boucles For Each en plaçant une boucle dans une autre.

L’exemple suivant illustre les structures For EachNext imbriquées.

' 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

Lorsque vous imbriquez des boucles, chaque boucle doit avoir une variable unique element.

Vous pouvez également imbriquer différents types de structures de contrôle les unes dans les autres. Pour plus d’informations, consultez Structures de contrôle imbriquées.

Exit For et Continue For

L’instruction Exit For entraîne l’exécution à quitter la boucle For...Next et transfère le contrôle à l’instruction qui suit l’instruction Next.

L’instruction Continue For transfère immédiatement le contrôle à l’itération suivante de la boucle. Pour plus d’informations, consultez Continuer l’instruction.

L’exemple suivant montre comment utiliser les instructions Continue For et 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

Vous pouvez placer n’importe quel nombre d’instructions Exit For dans une boucle For Each. Lorsqu’elle est utilisée dans des boucles imbriquées For Each , Exit For fait que l’exécution quitte la boucle la plus interne et transfère le contrôle vers le niveau d’imbrication supérieur suivant.

Exit For est souvent utilisée après une évaluation de certaines conditions, par exemple, dans une structure If...Then ...Else. Vous pouvez utiliser Exit For pour les conditions suivantes :

  • Il est inutile ou impossible de continuer à itérer. Cela peut être dû à une valeur erronée ou à une demande de résiliation.

  • Une exception est interceptée dans un Try...Catch ...Finally. Vous pouvez utiliser Exit For à la fin du bloc Finally.

  • Il s’agit d’une boucle sans fin, qui est une boucle qui peut s’exécuter un nombre important, voire infini de fois. Si vous détectez une telle condition, vous pouvez utiliser Exit For pour échapper à la boucle. Pour plus d’informations, consultez Instruction Do... Loop.

Iterators

Un itérateur est utilisé pour exécuter une itération personnalisée sur une collection. Un itérateur peut être une fonction ou un accesseur Get. Il utilise une instruction Yield pour retourner chaque élément de la collection un par un.

Vous appelez un itérateur en utilisant une instruction For Each...Next. Chaque itération de la boucle For Each appelle l’itérateur. Quand une instruction Yield est atteinte dans l’itérateur, une expression dans l’instruction Yield est retournée et la localisation actuelle dans le code est retenue. L’exécution est redémarrée à partir de cet emplacement la prochaine fois que l’itérateur est appelé.

L’exemple suivant utilise une fonction d’itérateur. La fonction d’itérateur a une instruction Yield qui se trouve à l’intérieur d’une boucle For…Next. Dans la méthode ListEvenNumbers, chaque itération du corps d’instruction For Each crée un appel à la fonction d’itérateur, qui poursuit avec l’instruction Yield suivante.

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

Pour plus d’informations, consultez Itérateurs, Instruction d’interruption et Itérateur.

Implémentation technique

Après l’exécution d’une instruction For EachNext, Visual Basic n’évalue la collection qu’une seule fois, avant que la boucle ne démarre. Si votre bloc d’instruction modifie element ou group, ces modifications n’affectent pas l’itération de la boucle.

Lorsque tous les éléments de la collection ont été affectés successivement à element, la boucle For Each s’arrête et le contrôle passe à l’instruction qui suit l’instruction Next.

Si Option Infer est activée (elle l’est par défaut), le compilateur de Visual Basic peut déduire le type de données de element. Si elle est désactivée et element n’a pas été déclaré en dehors de la boucle, vous devez la déclarer dans l’instruction For Each . Pour déclarer explicitement le type de données de element, utilisez une clause As. Sauf si le type de données de l’élément est défini en dehors de la construction For Each...Next , son étendue est le corps de la boucle. Notez que vous ne pouvez pas déclarer element à la fois à l’extérieur et à l’intérieur de la boucle.

Vous pouvez éventuellement spécifier element dans l’instruction Next. Cela améliore la lisibilité de votre programme, en particulier si vous avez des boucles For Each imbriquées. Vous devez spécifier la même variable que celle qui apparaît dans l’instruction For Each correspondante.

Il se peut que vous vouliez éviter de modifier la valeur de à l’intérieur d’une boucle element. Cela peut rendre plus difficiles la lecture et le débogage de votre code. La modification de la valeur de group n’affecte pas la collection ou ses éléments, qui ont été déterminés lors de la première entrée de la boucle.

Lorsque vous imbriquez des boucles, si une instruction Next d’un niveau d’imbrication externe est rencontrée avant le Next d’un niveau interne, le compilateur signale une erreur. Toutefois, le compilateur peut détecter cette erreur de chevauchement uniquement si vous spécifiez element dans chaque instruction Next.

Si votre code dépend de la traversée d’une collection dans un ordre particulier, une boucle For Each...Next n’est pas le meilleur choix, sauf si vous connaissez les caractéristiques de l’objet énumérateur exposé par la collection. L’ordre de traversée n’est pas déterminé par Visual Basic, mais par la méthode MoveNext de l’objet énumérateur. Par conséquent, il se peut que vous ne puissiez pas prédire quel élément de la collection est le premier à retourner dans element, ou quel est le suivant à retourner après un élément donné. Vous pourriez obtenir des résultats plus fiables à l’aide d’une structure de boucle différente, telle que For...Next ou Do...Loop.

Le runtime doit être en mesure de convertir les éléments dans group en element. L’instruction [Option Strict] contrôle si les conversions extensives et restrictives sont autorisées (Option Strict est désactivée par défaut) ou si seules les conversions extensives sont autorisées (Option Strict est activée). Pour plus d’informations, consultez Conversions restrictives.

Le type de données de group doit être un type de référence qui fait référence à une collection ou à un tableau énumérable. Le plus souvent, cela signifie que group fait référence à un objet qui implémente l’interface IEnumerable de l’espace de noms System.Collections ou l’interface IEnumerable<T> de l’espace de noms System.Collections.Generic. System.Collections.IEnumerable définit la méthode GetEnumerator, qui retourne un objet énumérateur pour la collection. L’objet énumérateur implémente l’interface System.Collections.IEnumerator de l’espace de noms System.Collections et expose la propriété Current et les méthodes Reset et MoveNext . Visual Basic les utilise pour parcourir la collection.

conversions restrictives

Lorsque Option Strict a la valeur On, les conversions restrictives entraînent généralement des erreurs du compilateur. Dans une instruction For Each, toutefois, les conversions des éléments dans group en element sont évaluées et effectuées au moment de l’exécution, et les erreurs du compilateur provoquées par les conversions restrictives sont supprimées.

Dans l’exemple suivant, l’affectation de m comme valeur initiale pour n ne se compile pas quand Option Strict est activée, car la conversion d’un Long en un Integer est une conversion restrictive. Toutefois, dans l’instruction For Each, aucune erreur du compilateur n’est signalée, même si l’affectation à number nécessite la même conversion de Long en Integer. Dans l’instruction For Each qui contient un grand nombre, une erreur d’exécution se produit lorsque ToInteger est appliqué au grand nombre.

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

Appels IEnumerator

Lorsque l’exécution d’une boucle For Each...Next démarre, Visual Basic vérifie que group fait référence à un objet de collection valide. Si ce n’est pas le cas, il lève une exception. Sinon, il appelle la méthode MoveNext et la propriété Current de l’objet énumérateur pour retourner le premier élément. Si MoveNext indique qu’il n’existe aucun élément suivant, autrement dit, si la collection est vide, la boucle For Each s’arrête et le contrôle passe à l’instruction qui suit l’instruction Next. Sinon, Visual Basic définit element au premier élément et exécute le bloc d’instructions.

Chaque fois que Visual Basic rencontre l’instruction Next, il retourne à l’instruction For Each . Là encore, il appelle MoveNext et Current pour retourner l’élément suivant, et à nouveau il exécute le bloc ou arrête la boucle en fonction du résultat. Ce processus se poursuit jusqu’à ce que MoveNext indique qu’il n’y a pas d’élément suivant ou qu’une instruction Exit For est rencontrée.

Modification de la collection. L’objet énumérateur retourné par GetEnumerator ne vous permet normalement pas de modifier la collection en ajoutant, en supprimant, en remplaçant ou en réorganisant des éléments. Si vous modifiez la collection après avoir lancé une boucle For Each...Next , l’objet énumérateur devient non valide et la prochaine tentative d’accès à un élément provoque une exception InvalidOperationException.

Toutefois, ce blocage de modification n’est pas déterminé par Visual Basic, mais plutôt par l’implémentation de l’interface IEnumerable. Il est possible d’implémenter IEnumerable d’une manière qui autorise la modification pendant l’itération. Si vous envisagez d’effectuer une telle modification dynamique, assurez-vous de bien comprendre les caractéristiques de l’implémentation IEnumerable sur la collection que vous utilisez.

Modification d’éléments de collection. La propriété Current de l’objet énumérateur est ReadOnly et retourne une copie locale de chaque élément de collection. Cela signifie que vous ne pouvez pas modifier les éléments eux-mêmes dans une boucle For Each...Next . Toute modification que vous apportez affecte uniquement la copie locale à partir de Current et n’est pas répercutée dans la collection sous-jacente. Toutefois, si un élément est un type référence, vous pouvez modifier les membres de l’instance vers lesquels il pointe. L’exemple suivant modifie le membre BackColor de chaque élément thisControl. Toutefois, vous ne pouvez pas vous modifier thisControl lui-même.

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’exemple précédent peut modifier le membre BackColor de chaque élément thisControl, bien qu’il ne puisse pas modifier thisControl lui-même.

Traversée des tableaux. Étant donné que la classe Array implémente l’interface IEnumerable, tous les tableaux exposent la méthode GetEnumerator. Cela signifie que vous pouvez itérer au sein d’un tableau avec une boucle For Each...Next . Toutefois, vous pouvez uniquement lire les éléments du tableau. Vous ne pouvez pas les modifier.

Exemple 1

L’exemple suivant liste tous les dossiers du répertoire C:\ à l’aide de 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

Exemple 2

L’exemple suivant illustre une procédure de tri d’une collection. L’exemple trie les instances d’une classe Car stockées dans une List<T>. La classe Car implémente l’interface IComparable<T>, ce qui implique l’implémentation de la méthode CompareTo.

Chaque appel à la méthode CompareTo effectue une comparaison unique qui est utilisée pour le tri. Le code écrit par l’utilisateur dans la méthode CompareTo retourne une valeur pour chaque comparaison de l’objet actuel avec un autre objet. La valeur retournée est inférieure à zéro si l’objet actuel est inférieur à l’autre objet, supérieure à zéro l’objet actuel est supérieur à l’autre et égale à zéro s’ils sont égaux. Cela vous permet de définir dans le code les critères définissant « supérieur à », « inférieur à » et « égal à ».

Dans la méthode ListCars, l’instruction cars.Sort() trie la liste. Cet appel à la méthode Sort de List<T> entraîne l’appel automatique de la méthode CompareTo pour les objets Car dans 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

Voir aussi