Instrucciones de iteración: for
, foreach
, do
y while
Las instrucciones de iteración ejecutan repetidamente una instrucción o un bloque de instrucciones. La instrucción for
: ejecuta su cuerpo mientras una expresión booleana especificada se evalúe como true
. La instrucción foreach
: enumera los elementos de una colección y ejecuta su cuerpo para cada elemento de la colección. La instrucción do
: ejecuta condicionalmente su cuerpo una o varias veces. La instrucción while
: ejecuta condicionalmente su cuerpo cero o varias veces.
En cualquier punto del cuerpo de una instrucción de iteración, se puede salir del bucle mediante la instrucción break
. Puede ir paso a paso a la siguiente iteración del bucle mediante la instrucción continue
.
Instrucción for
La instrucción for
ejecuta una instrucción o un bloque de instrucciones mientras una expresión booleana especificada se evalúa como true
. En el ejemplo siguiente se muestra la instrucción for
, que ejecuta su cuerpo mientras que un contador entero sea menor que tres:
for (int i = 0; i < 3; i++)
{
Console.Write(i);
}
// Output:
// 012
En el ejemplo anterior se muestran los elementos de la instrucción for
:
La sección inicializador, que se ejecuta solo una vez, antes de entrar en el bucle. Normalmente, se declara e inicializa una variable de bucle local en esa sección. No se puede acceder a la variable declarada desde fuera de la instrucción
for
.La sección inicializador del ejemplo anterior declara e inicializa una variable de contador entero:
int i = 0
La sección condición que determina si se debe ejecutar la siguiente iteración del bucle. Si se evalúa como
true
o no está presente, se ejecuta la siguiente iteración; de lo contrario, se sale del bucle. La sección condición debe ser una expresión booleana.La sección condición del ejemplo anterior comprueba si un valor de contador es menor que tres:
i < 3
La sección iterador, que define lo que sucede después de cada iteración del cuerpo del bucle.
La sección iterador del ejemplo anterior incrementa el contador:
i++
El cuerpo del bucle, que es una instrucción o un bloque de instrucciones.
La sección iterador puede contener cero o más de las siguientes expresiones de instrucción, separadas por comas:
- expresión de incremento de prefijo o sufijo, como
++i
oi++
- expresión de decremento de prefijo o sufijo, como
--i
oi--
- asignación
- invocación de un método
- Expresión
await
- Creación de un objeto mediante el operador
new
.
Si no declara una variable de bucle en la sección inicializador, también puede usar cero o varias de las expresiones de la lista anterior de dicha sección. En el ejemplo siguiente se muestran varios usos menos comunes de las secciones inicializador e iterador: asignar un valor a una variable externa en la sección inicializador, invocar un método en las secciones inicializador e iterador, y cambiar los valores de dos variables en la sección iterador:
int i;
int j = 3;
for (i = 0, Console.WriteLine($"Start: i={i}, j={j}"); i < j; i++, j--, Console.WriteLine($"Step: i={i}, j={j}"))
{
//...
}
// Output:
// Start: i=0, j=3
// Step: i=1, j=2
// Step: i=2, j=1
Todas las secciones de la instrucción for
son opcionales. En el ejemplo, el siguiente código define el bucle for
infinito:
for ( ; ; )
{
//...
}
Instrucción foreach
La instrucción foreach
ejecuta una instrucción o un bloque de instrucciones para cada elemento de una instancia del tipo que implementa la interfaz System.Collections.IEnumerable o System.Collections.Generic.IEnumerable<T>, como se muestra en el siguiente ejemplo:
List<int> fibNumbers = new() { 0, 1, 1, 2, 3, 5, 8, 13 };
foreach (int element in fibNumbers)
{
Console.Write($"{element} ");
}
// Output:
// 0 1 1 2 3 5 8 13
La instrucción foreach
no está limitada a esos tipos. Puede usarla con una instancia de cualquier tipo que cumpla las condiciones siguientes:
- Un tipo tiene el método público
GetEnumerator
sin parámetros. El métodoGetEnumerator
puede ser el método de extensión de un tipo. - El tipo de valor devuelto del método
GetEnumerator
tiene la propiedad públicaCurrent
y el método públicoMoveNext
sin parámetros, cuyo tipo de valor devuelto esbool
.
En el siguiente ejemplo se usa la instrucción foreach
con una instancia del tipo System.Span<T>, que no implementa ninguna interfaz:
Span<int> numbers = [3, 14, 15, 92, 6];
foreach (int number in numbers)
{
Console.Write($"{number} ");
}
// Output:
// 3 14 15 92 6
Si la propiedad Current
del enumerador devuelve un valor devuelto de referencia (ref T
donde T
es el tipo de un elemento de colección), puede declarar una variable de iteración con el modificador ref
o ref readonly
, como se muestra en el ejemplo siguiente:
Span<int> storage = stackalloc int[10];
int num = 0;
foreach (ref int item in storage)
{
item = num++;
}
foreach (ref readonly var item in storage)
{
Console.Write($"{item} ");
}
// Output:
// 0 1 2 3 4 5 6 7 8 9
Si la colección de origen de la instrucción foreach
está vacía, el cuerpo de la instrucción foreach
no se ejecuta y se omite. Si la instrucción foreach
se aplica a null
, se produce NullReferenceException.
await foreach
Puede usar la instrucción await foreach
para consumir un flujo asincrónico de datos, es decir, el tipo de colección que implementa la interfaz IAsyncEnumerable<T>. Cada iteración del bucle se puede suspender mientras el siguiente elemento se recupera de forma asincrónica. En el ejemplo siguiente se muestra cómo usar la instrucción await foreach
:
await foreach (var item in GenerateSequenceAsync())
{
Console.WriteLine(item);
}
También puede usar la instrucción await foreach
con una instancia de cualquier tipo que cumpla las condiciones siguientes:
- Un tipo tiene el método público
GetAsyncEnumerator
sin parámetros. Ese método puede ser el método de extensión del tipo. - El tipo de valor devuelto del método
GetAsyncEnumerator
tiene la propiedad públicaCurrent
y el método públicoMoveNextAsync
sin parámetros cuyo tipo de valor devuelto esTask<bool>
,ValueTask<bool>
, o cualquier otro tipo que se puede esperar cuyo métodoGetResult
de awaiter devuelve un valorbool
.
Los elementos de secuencia se procesan de forma predeterminada en el contexto capturado. Si quiere deshabilitar la captura del contexto, use el método de extensión TaskAsyncEnumerableExtensions.ConfigureAwait. Para obtener más información sobre los contextos de sincronización y la captura del contexto actual, vea Utilizar el modelo asincrónico basado en tareas. Para obtener más información sobre las secuencias asincrónicas, consulte Tutorial de secuencias asincrónicas.
Tipo de una variable de iteración
Puede usar la palabra clave var
para permitir que el compilador infiera el tipo de una variable de iteración de la instrucción foreach
, como se muestra en el código siguiente:
foreach (var item in collection) { }
Nota:
El compilador puede deducir el tipo de var
como un tipo de referencia que acepta valores NULL, dependiendo de si el contexto compatible con valores NULL está habilitado y si el tipo de una expresión de inicialización es un tipo de referencia.
Para obtener más información, vea variables locales con tipo implícito.
También puede especificar de forma explícita el tipo de una variable de iteración, como se muestra en el código siguiente:
IEnumerable<T> collection = new T[5];
foreach (V item in collection) { }
En el formulario anterior, el tipo T
de un elemento de colección se debe poder convertir de forma implícita o explícita en el tipo V
de una variable de iteración. Si se produce un error en una conversión explícita de T
en V
en tiempo de ejecución, la instrucción foreach
produce InvalidCastException. Por ejemplo, si T
es un tipo de clase no sellada, V
puede ser cualquier tipo de interfaz, incluso uno que T
no implemente. En tiempo de ejecución, el tipo de un elemento de colección puede ser el que deriva de T
y realmente implementa V
. Si ese no es el caso, se produce InvalidCastException.
Instrucción do
La instrucción do
ejecuta una instrucción o un bloque de instrucciones mientras que una expresión booleana especificada se evalúa como true
. Como esa expresión se evalúa después de cada ejecución del bucle, un bucle do
se ejecuta una o varias veces. El bucle do
difiere del bucle while
, que se ejecuta cero o varias veces.
En el ejemplo siguiente se muestra el uso de la instrucción do
:
int n = 0;
do
{
Console.Write(n);
n++;
} while (n < 5);
// Output:
// 01234
Instrucción while
La instrucción while
ejecuta una instrucción o un bloque de instrucciones mientras que una expresión booleana especificada se evalúa como true
. Como esa expresión se evalúa antes de cada ejecución del bucle, un bucle while
se ejecuta cero o varias veces. El bucle while
difiere del bucle do
, que se ejecuta una o varias veces.
En el ejemplo siguiente se muestra el uso de la instrucción while
:
int n = 0;
while (n < 5)
{
Console.Write(n);
n++;
}
// Output:
// 01234
Especificación del lenguaje C#
Para más información, vea las secciones siguientes de la Especificación del lenguaje C#:
Para obtener más información sobre estas características, consulte las siguientes notas de propuesta de características: