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:

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 = [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étodo GetEnumerator puede ser el método de extensión de un tipo.
  • El tipo de valor devuelto del método GetEnumerator tiene la propiedad pública Current y el método público MoveNext sin parámetros, cuyo tipo de valor devuelto es bool.

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ública Currenty el método público MoveNextAsync sin parámetros cuyo tipo de valor devuelto es Task<bool>, ValueTask<bool>, o cualquier otro tipo que se puede esperar cuyo método GetResult de awaiter devuelve un valor bool.

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:

Consulte también