Instrucción For Each...Next (Visual Basic)

Repite un grupo de instrucciones para cada elemento de una colección.

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

Elementos

Término

Definición

element

Se requiere en la instrucción For Each. Opcional en la instrucción Next. Variable. Se usa para recorrer en iteración los elementos de la colección.

datatype

Requerido si element no ya se ha declarado. Tipo de datos de element.

group

Requerido. Una variable con un tipo que es un tipo de colección o un objeto. Hace referencia a la colección sobre la que se debe repetir la ejecución de statements.

statements

Opcional. Una o más instrucciones entre For Each y Next que se ejecutan en cada elemento de group.

Continue For

Opcional. Transfiere el control al principio del bucle For Each.

Exit For

Opcional. Transfiere el control fuera del bucle For Each.

Next

Requerido. Termina la definición del bucle For Each.

Ejemplo simple

Utilice un bucle For Each...Next cuando desee repetir un conjunto de instrucciones para cada elemento de una colección o matriz.

Sugerencia

Una Instrucción For...Next (Visual Basic) funciona bien cuando se puede asociar cada iteración de un bucle con una variable de control y determinar los valores iniciales y finales de esa variable.Sin embargo, cuando está trabajando con una colección, el concepto de inicial y de configuración final no es significativo, y no conoce necesariamente cuántos elementos tiene la colección.En este tipo de caso, un bucle de For Each…Next suele ser una opción mejor.

En el siguiente ejemplo, la instrucción de For Each…Next recorre en iteración todos los elementos de una colección 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

Para obtener más ejemplos, vea Colecciones (C# y Visual Basic) y Matrices en Visual Basic.

Bucles anidados

Se pueden anidar bucles For Each colocando un bucle dentro de otro.

El ejemplo siguiente demuestra estructuras For Each...Next anidadas.

' 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 

Cuando se anida bucles, cada bucle debe tener una variable única de element.

También puede anidar distintos tipos de estructuras de control unos dentro de otros. Para obtener más información, vea Estructuras de control anidadas (Visual Basic).

Exit For y Continue For

La ejecución de las causas de la instrucción de Exit For salir del bucle de For…Next y transfiere el control a la instrucción que sigue a la instrucción de Next.

la instrucción Continue For transfiere el control inmediatamente a la siguiente iteración del bucle. Para obtener más información, vea Continue (Instrucción, Visual Basic).

En el siguiente ejemplo se muestra cómo utilizar las instrucciones Continue For y 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 7, 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

Puede colocar cualquier número de instrucciones Exit For en un bucle For Each . Cuando se utiliza dentro de bucles For Each anidados, Exit For hace que la ejecución salga del bucle más profundo y transfiera el control al siguiente nivel de anidamiento.

Exit For se utiliza a menudo después de evaluar alguna condición, por ejemplo, en una estructura If...Then...Else. Puede que desee utilizar Exit For para las siguientes condiciones:

  • Continuar la iteración es innecesario o imposible. Esto podría deberse a un valor erróneo o una solicitud de terminación.

  • Se detecta una excepción en Try...Catch...Finally. Puede utilizar Exit For al final del bloque Finally .

  • Hay un bucle infinito, que es un bucle que se podría ejecutar muchas o incluso infinitas veces. Si se detecta este tipo de condición, se puede utilizar Exit For para escapar del bucle. Para obtener más información, vea Instrucción Do...Loop (Visual Basic).

Iteradores

Utiliza un iterador para realizar una iteración personalizada en una colección. Un iterador puede ser una función o un descriptor de acceso de Get. Utilice una instrucción de Yield para devolver cada elemento de la colección de uno en uno.

Se llama a un iterador mediante una instrucción For Each...Next. Cada iteración del bucle For Each llama el iterador. Cuando una instrucción de Yield se consigue en el iterador, la expresión en la instrucción de Yield se devuelve, y la ubicación actual en el código se conserva. La ejecución se reinicia desde esta ubicación la próxima vez que se llame al iterador.

El ejemplo siguiente utiliza una función de iterador. La función de iterador tiene una instrucción de Yield que está dentro de un bucle de For… Next. En el método de ListEvenNumbers, cada iteración del cuerpo de instrucción de For Each crea una llamada a la función de iterador, que continúa a Yield la instrucción siguiente.

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

Para obtener más información, vea Iteradores (C# y Visual Basic), Yield (Instrucción) (Visual Basic) y Iterador (Visual Basic).

Implementación técnica

Cuando una instrucción de For Each…Next ejecuta, Visual Basic evalúa la colección sólo una vez, antes de que el bucle se inicie. Si el bloque de instrucciones cambia element o group, estos cambios no afectan a la iteración del bucle.

Cuando todos los elementos de la colección se han asignado correctamente a element, el bucle For Each se detiene y el control pasa a la instrucción que sigue a la instrucción Next.

Si element no se ha declarado fuera de este bucle, éste debe ser en la instrucción de For Each. Puede declarar el tipo de element explícitamente mediante una instrucción As o bien puede basarse en la inferencia de tipos para asignar el tipo. En ambos casos, el ámbito de element es el cuerpo del bucle. Sin embargo, no se puede declarar element fuera y dentro del bucle.

Opcionalmente, puede especificarse element en la instrucción Next. Esto mejora la legibilidad del programa, sobre todo si se han anidado bucles For Each. La variable que se especifique debe ser igual a la que aparece en la instrucción For Each correspondiente.

Puede que desee evitar cambiar el valor de element dentro de un bucle. Esto puede hacer más difícil leer y depurar el código. Cambiar el valor de group no afecta a la colección o sus elementos, que se determinan cuando el bucle primero se escribió.

Cuando está bucles de anidamiento, si una instrucción de Next de un nivel de anidamiento externo se encuentra antes de que Next de un nivel interno, el compilador señala un error. Sin embargo, el compilador sólo puede detectar este error superpuesto si se especifica element en cada instrucción Next.

Si el código depende de recorrer una colección en un orden concreto, un bucle de For Each…Next no es la mejor opción, a menos que sepa las características del objeto de enumerador que la colección expone. El orden de recorrido no está determinada por Visual Basic, sino por el método de MoveNext del objeto de enumerador. Por tanto, tal vez no pueda predecir qué elemento de la colección es el primero que se devuelve en element o qué elemento es el siguiente en ser devuelto tras un elemento dado. Es posible obtener resultados más confiables si se utiliza una estructura de bucle diferente, tal como For...Next o Do...Loop.

El tipo de datos de element debe ser tal que el tipo de datos de los elementos de group se pueda convertir en él.

El tipo de datos de group debe ser un tipo de referencia que hace referencia a una colección o a una matriz que es enumerable. Normalmente, esto significa que group hace referencia a un objeto que implementa la interfaz IEnumerable del espacio de nombres System.Collections o la interfaz IEnumerable del espacio de nombres System.Collections.Generic. System.Collections.IEnumerable define el método GetEnumerator, que devuelve un objeto enumerador para la colección. El objeto enumerador implementa la interfaz System.Collections.IEnumerator del espacio de nombres System.Collections y expone la propiedad Current y los métodos Reset y MoveNext. Visual Basic los utiliza para recorrer la colección.

Conversiones de restricción

Cuando Option Strict está establecido en On, las conversiones de restricción suelen producir errores del compilador. Sin embargo, en una instrucción For Each, las conversiones de los elementos de group a element se evalúan y realizan en tiempo de ejecución, y se suprimen los errores del compilador causados por las conversiones de restricción.

En el ejemplo siguiente, la asignación de m como valor inicial para n no compila a Option Strict está activado porque la conversión de Long a Integer es una conversión de restricción. En la instrucción For Each, sin embargo, no se informa de ningún error del compilador, a pesar de que la asignación a number requiere la misma conversión de Long a Integer. En la instrucción For Each que contiene un gran número, se produce un error en tiempo de ejecución cuando ToInteger se aplica al gran número.

Option Strict On 

Module Module1
    Sub Main()
        ' 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

        Console.ReadKey()
    End Sub 
End Module

Llamadas a IEnumerator

Cuando comienza la ejecución de un bucle For Each...Next, Visual Basic comprueba que group hace referencia a un objeto de colección válido. Si no, produce una excepción. De lo contrario, llama al método MoveNext y a la propiedad Current del objeto enumerador para devolver el primer elemento. Si MoveNext indica que no hay un elemento siguiente, es decir, si la colección está vacía, el bucle For Each se detiene y el control pasa a la instrucción que sigue a Next. De lo contrario, Visual Basic establece element en el primer elemento y ejecuta el bloque de instrucciones.

Cada vez que Visual Basic encuentra la instrucción Next, vuelve a la instrucción For Each. Llama de nuevo a MoveNext y Current para devolver el elemento siguiente, y una vez más ejecuta el bloque o se detiene el bucle según el resultado. Este proceso continúa hasta que MoveNext indica que no hay ningún elemento siguiente o se encuentra una instrucción Exit For.

Modificar la colección. El objeto de enumerador devuelto por GetEnumerator no permite normalmente cambiar la colección agregar, eliminar, reemplazando, o si ningún elemento. Si se modifica la colección después de haber iniciado un bucle For Each...Next, el objeto enumerador deja de ser válido y el siguiente intento de acceso a un elemento produce una excepción InvalidOperationException.

Sin embargo, este bloqueo de la modificación no está determinado por Visual Basic, sino por la implementación de la interfaz de IEnumerable. Es posible implementar IEnumerable de modo que se permita la modificación durante la iteración. Si piensa realizar este tipo de modificación dinámica, asegúrese de que conoce las características de la implementación de IEnumerable en la colección que está utilizando.

Modificar los elementos de colección. La propiedad Current del objeto enumerador es ReadOnly (Visual Basic) y devuelve una copia local de cada elemento de la colección. Esto significa que no es posible modificar los propios elementos en un bucle For Each...Next. Cualquier modificación que cree solo afecta a la copia local de Current y no se refleja de nuevo en la colección subyacente. Sin embargo, si un elemento es un tipo de referencia, es posible modificar los miembros de la instancia a los que apunta. El ejemplo siguiente se modifica el miembro de BackColor de cada elemento de thisControl. No puede, sin embargo, modificar thisControl propio.

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

En el ejemplo anterior se puede modificar el miembro BackColor de cada elemento thisControl, aunque no se puede modificar el propio elemento thisControl.

Recorrer matrices. Dado que la clase Array implementa la interfaz IEnumerable, todas las matrices exponen el método GetEnumerator. Esto significa que es posible recorrer una matriz con un bucle For Each...Next. Sin embargo, solo se pueden leer los elementos de la matriz. No se pueden cambiar.

Ejemplo

El ejemplo siguiente muestra todas las carpetas del directorio C:\ utilizando la clase DirectoryInfo .

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

El ejemplo siguiente muestra un procedimiento para ordenar una colección. El ejemplo ordena las instancias de una clase de Car almacenados en List. La clase Car implementa la interfaz IComparable, que requiere que el método CompareTo se implemente.

Cada llamada al método de CompareTo crea una única comparación que se utiliza para ordenar. El código escrito por el usuario en el método CompareTo devuelve un valor para cada comparación del objeto actual con otro objeto. El valor devuelto es menor que cero si el objeto actual es menor que otro objeto, mayor que cero si el objeto actual es mayor que otro objeto y cero si son iguales. Esto permite definir en el código los criterios para la evaluación mayor que, menor que, e igual que.

En el método ListCars, la expresión cars.Sort() ordena la lista. Esta llamada al método Sort de List hace que el método CompareTo sea llamado automáticamente para los objetos de clase Car en 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

Vea también

Referencia

Instrucción For...Next (Visual Basic)

Instrucción While...End While (Visual Basic)

Instrucción Do...Loop (Visual Basic)

Conceptos

Estructuras de bucles (Visual Basic)

Conversiones de ampliación y de restricción (Visual Basic)

Inicializadores de objeto: Tipos con nombre y anónimos (Visual Basic)

Inicializadores de colección (Visual Basic)

Otros recursos

Colecciones (C# y Visual Basic)

Matrices en Visual Basic