Instrucción For Each...Next (Visual Basic)
Repite un grupo de instrucciones para cada elemento de una colección.
Sintaxis
For Each element [ As datatype ] In group
[ statements ]
[ Continue For ]
[ statements ]
[ Exit For ]
[ statements ]
Next [ element ]
Partes
Término | Definición |
---|---|
element |
Obligatorio en la instrucción For Each . Opcional en la instrucción Next . Variable. Se usa para iterar por los elementos de la colección. |
datatype |
Opcional si Option Infer está activado (valor predeterminado) o element ya está declarado; obligatorio si Option Infer está desactivado y element aún no está declarado. El tipo de datos de element . |
group |
Necesario. Variable con un tipo que es un objeto o un tipo de colección. Hace referencia a la colección en la statements que se va a repetir. |
statements |
Opcional. Una o varias instrucciones entre For Each y Next que se ejecutan en cada elemento de group . |
Continue For |
Opcional. Transfiere el control al inicio del bucle For Each . |
Exit For |
Opcional. Transfiere el control fuera del bucle For Each . |
Next |
Necesario. Termina la definición del bucle For Each . |
Ejemplo sencillo
Use un bucle For Each
...Next
cuando quiera repetir un conjunto de instrucciones para cada elemento de una colección o matriz.
Sugerencia
Una instrucción For...Next 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 la variable. Sin embargo, cuando se trabaja con una colección, el concepto de valores iniciales y finales no es significativo y no necesariamente se sabe cuántos elementos tiene la colección. En este tipo de caso, un bucle For Each
...Next
suele ser una mejor opción.
En el ejemplo siguiente, la instrucción For Each
…Next
itera por todos los elementos de una colección de lista.
' 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, consulte Colecciones y Matrices.
Nested Loops
Puede anidar bucles For Each
si coloca un bucle dentro de otro.
En el siguiente ejemplo se muestra 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
Al anidar bucles, cada bucle debe tener una variable única element
.
También puede anidar diferentes tipos de estructuras de control entre sí. Para obtener más información, consulte Estructuras de control anidadas.
Exit For y Continue For
La instrucción Exit For hace que la ejecución salga del bucle For
…Next
y transfiere inmediatamente el control a la instrucción que hay después de la instrucción Next
.
La instrucción Continue For
transfiere inmediatamente el control a la siguiente iteración del bucle. Para obtener más información, consulte Continue (Instrucción).
En el ejemplo siguiente se muestra cómo usar 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 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
Puede colocar cualquier número de instrucciones Exit For
en un bucle For Each
. Cuando se usa en bucles For Each
anidados, Exit For
hace que la ejecución salga del bucle más interno y transfiera el control al siguiente nivel superior de anidamiento.
Exit For
a menudo se usa después de una evaluación de alguna condición, por ejemplo, en una estructura If
...Then
...Else
. Es posible que quiera usar Exit For
para las condiciones siguientes:
Seguir iterando es innecesario o imposible. Esto puede deberse a un valor erróneo o a una solicitud de finalización.
Se detecta una excepción en un objeto
Try
...Catch
...Finally
. Puede usarExit For
al final del bloqueFinally
.Hay un bucle infinito, que es un bucle que podría ejecutarse un número grande o incluso infinito de veces. Si detecta esta condición, puede usar
Exit For
para escapar del bucle. Para más información, consulte Do...Loop (Instrucción).
Iterators
Un iterador se usa para realizar una iteración personalizada en una colección. Un iterador puede ser una función o un descriptor de acceso Get
. Un iterador usa una instrucción Yield
para devolver cada elemento de la colección de uno en uno.
Llame a un iterador mediante una instrucción For Each...Next
. Cada iteración del bucle For Each
llama al iterador. Cuando se llega a una instrucción Yield
en el iterador, se devuelve una expresión en la instrucción Yield
y se conserva la ubicación actual en el código. La ejecución se reinicia desde esa ubicación la próxima vez que se llama al iterador.
El siguiente ejemplo usa una función de iterador. La función de iterador tiene una instrucción Yield
que está dentro de un bucle For…Next. En el método ListEvenNumbers
, cada iteración del cuerpo de la instrucción For Each
crea una llamada a la función de iterador, que continúa a la instrucción Yield
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, consulte Iteradores, Yield (Instrucción) e Iterador.
Implementación técnica
Cuando se ejecuta una instrucción For Each
…Next
, Visual Basic evalúa la colección una sola vez antes de que se inicie el bucle. Si la instrucción bloquea los cambios element
o group
, estos cambios no afectan a la iteración del bucle.
Cuando todos los elementos de la colección se han asignado sucesivamente a element
, el bucle For Each
se detiene y el control pasa a la instrucción que sigue a la instrucción Next
.
Si Option Infer está activado (su valor predeterminado), el compilador de Visual Basic puede deducir el tipo de datos de element
. Si está desactivado y element
no se ha declarado fuera del bucle, debe declararlo en la instrucción For Each
. Para declarar el tipo de datos de element
explícitamente, use una cláusula As
. A menos que el tipo de datos del elemento se defina fuera de la construcción For Each
...Next
, su ámbito es el cuerpo del bucle. Tenga en cuenta que no puede declarar element
tanto fuera como dentro del bucle.
Opcionalmente, puede especificar element
en la instrucción Next
. Esto mejora la legibilidad del programa, especialmente si tiene bucles For Each
anidados. Debe especificar la misma variable que la que aparece en la instrucción For Each
correspondiente.
Es posible que quiera evitar cambiar el valor de element
dentro de un bucle. Esto puede dificultar la lectura y depuración del código. Cambiar el valor de group
no afecta a la colección o a sus elementos, que se determinaron cuando se especificó el bucle por primera vez.
Al anidar bucles, si se encuentra una instrucción Next
de un nivel de anidamiento externo antes de Next
de un nivel interno, el compilador marca un error. Sin embargo, el compilador solo puede detectar este error de superposición si se especifica element
en cada instrucción Next
.
Si el código depende de recorrer una colección en un orden determinado, un bucle For Each
...Next
no es la mejor opción, a menos que conozca las características del objeto enumerador que expone la colección. El orden del recorrido no viene determinado por Visual Basic, sino por el método MoveNext del objeto enumerador. Por lo tanto, es posible que no pueda predecir qué elemento de la colección es el primero que se devolverá en element
, o cuál es el siguiente que se devolverá después de un elemento determinado. Puede lograr resultados de mayor fiabilidad mediante una estructura de bucle diferente, como For
...Next
o Do
...Loop
.
El tiempo de ejecución debe poder convertir los elementos de group
a element
. La instrucción [Option Strict
] controla si se permiten conversiones de ampliación y restricción (Option Strict
significa desactivada, su valor predeterminado) o si solo se permiten conversiones de ampliación (Option Strict
significa activada). Para obtener más información, consulte Conversiones de restricción.
El tipo de datos de group
debe ser un tipo de referencia que haga referencia a una colección o a una matriz que pueda enumerarse. 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<T> 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 usa para recorrer la colección.
conversiones de restricción
Cuando Option Strict
se establece en On
, las conversiones de restricción normalmente provocan errores del compilador. Sin embargo, en una instrucción For Each
, las conversiones de los elementos de group
en element
se evalúan y se realizan en tiempo de ejecución, y se suprimen los errores del compilador causados por conversiones de restricción.
En el ejemplo siguiente, la asignación de m
como valor inicial de n
no se compila cuando Option Strict
está activado porque la conversión de Long
a Integer
es una conversión de restricción. Sin embargo, en la instrucción For Each
no se notifica ningún error del compilador, aunque la asignación a number
requiere la misma conversión de Long
a Integer
. En la instrucción For Each
que contiene un número elevado, se produce un error en tiempo de ejecución cuando ToInteger se aplica al número elevado.
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
Llamadas de IEnumerator
Cuando se inicia 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 es así, 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 ningún 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 la instrucción 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
. De nuevo llama a MoveNext
y Current
para devolver el siguiente elemento y, de nuevo, ejecuta el bloque o detiene el bucle en función del resultado. Este proceso continúa hasta que MoveNext
indica que no se encuentra ningún elemento siguiente o se encuentra una instrucción Exit For
.
Modificar la colección. El objeto enumerador devuelto por GetEnumerator normalmente no permite cambiar la colección agregando, eliminando, reemplazando o reordenando ningún elemento. Si cambia 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 acceder a un elemento produce una excepción InvalidOperationException.
Sin embargo, este bloqueo de modificación no viene determinado por Visual Basic, sino por la implementación de la interfaz IEnumerable. Es posible implementar IEnumerable
de forma que permita la modificación durante la iteración. Si está pensando en realizar esta modificación dinámica, asegúrese de comprender las características de la implementación IEnumerable
en la colección que está usando.
Modificar elementos de colección. La propiedad Current del objeto enumerador es ReadOnly y devuelve una copia local de cada elemento de colección. Esto significa que no se pueden modificar los propios elementos en un bucle For Each
...Next
. Cualquier modificación que realice afecta solo 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, puede modificar los miembros de la instancia a la que apunta. En el ejemplo siguiente se modifica el miembro BackColor
de cada elemento thisControl
. Sin embargo, no se puede modificar el propio 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
En el ejemplo anterior se puede modificar el miembro BackColor
de cada thisControl
elemento, aunque no se puede modificar el propio thisControl
.
Recorrido por las matrices. Dado que la clase Array implementa la interfaz IEnumerable, todas las matrices exponen el método GetEnumerator. Esto significa que puede iterar por una matriz con un bucle For Each
...Next
. Sin embargo, solo puede leer los elementos de la matriz. No puede cambiarlos.
Ejemplo 1
En el ejemplo siguiente se enumeran todas las carpetas del directorio C:\ mediante 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
Ejemplo 2
En el ejemplo siguiente se muestra un procedimiento para ordenar una colección. El ejemplo ordena las instancias de la clase Car
que se almacenan en List<T>. La clase Car
implementa la interfaz IComparable<T>, que requiere implementar el método CompareTo.
Cada llamada al método CompareTo realiza una comparación única que se usa para la ordenación. 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 el otro objeto, mayor que cero si el objeto actual es mayor que el otro objeto y cero si son iguales. Esto permite definir en el código los criterios de mayor que, menor que e igual.
En el método ListCars
, la instrucción cars.Sort()
ordena la lista. Esta llamada al método Sort de List<T> hace que se llame automáticamente al método CompareTo
para los objetos Car
de 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