Un bucle while repite su cuerpo mientras una condición siga siendo verdadera. Úsalo cuando no sabes de antemano cuántas iteraciones necesitas: sigue hasta que un saldo llegue a un objetivo, una cola se vacíe o se acabe la entrada.
Salida:
Year 1: 1070.00
Year 2: 1144.90
Year 3: 1225.04
Year 4: 1310.80
Year 5: 1402.55
Year 6: 1500.73
Goal reached after 6 years
La condición se comprueba antes de cada iteración, incluida la primera. Si savings ya alcanzara el objetivo, el cuerpo no se ejecutaría nunca y el programa imprimiría "after 0 years". El bucle depende de que el cuerpo cambie algo que lee la condición; aquí savings crece cada año hasta que savings < goal pasa a ser falso.
do while: ejecutar al menos una vez
Un bucle do while lleva la prueba al final, así que el cuerpo siempre se ejecuta al menos una vez:
Salida:
Attempt 1: got 7
Attempt 2: got 4
Attempt 3: got 1
Done after 3 attempts
El primer valor de code solo existe después de que el cuerpo se haya ejecutado una vez, que es justo cuando encaja un do while. Hay dos detalles de sintaxis que pillan a la gente:
- El punto y coma después de
while (code != 1)es obligatorio. Omitirlo es un error de compilación. - Las variables que usa la condición deben declararse antes de
do. Una variable declarada dentro de las llaves está fuera de ámbito cuando se evalúa la condición, así quedo { int code = ...; } while (code != 1);no compila.
El mismo bucle escrito con while necesita o bien una primera llamada duplicada antes del bucle, o bien un valor inicial ficticio que fuerce la primera pasada. do while expresa directamente "prueba, y luego decide si vuelves a probar".
Leer la entrada hasta un centinela
Leer líneas hasta una palabra de parada, o hasta que termine la entrada, es el bucle while de manual. Console.ReadLine() devuelve null cuando no hay más entrada, así que prueba las dos cosas:
Salida:
skipping "abc": not a number
3 amounts, total 49.50
(line = Console.ReadLine()) != null asigna la línea siguiente y la prueba en una sola expresión; los paréntesis alrededor de la asignación son obligatorios porque != tiene más precedencia que =. El bucle se detiene en done, así que el 99 que va después nunca se lee. decimal.TryParse rechaza abc sin lanzar una excepción, y continue salta a la línea siguiente. Sin la comprobación de null, llegar al final de la entrada pasaría null a TryParse para siempre, porque ReadLine sigue devolviendo null.
Un do while encaja con el patrón de preguntar y validar, en el que la pregunta debe aparecer al menos una vez:
Salida:
Enter your age:
"-4" is not a valid age
Enter your age:
"fifteen" is not a valid age
Enter your age:
Age accepted: 15
La pregunta está dentro del bucle, así que se repite después de cada respuesta rechazada, y age se declara antes de do para que el código posterior al bucle pueda usarla. Con un usuario en el teclado, cada respuesta aparece en la consola después de su pregunta; con la entrada redirigida, como aquí, solo se muestra la salida del propio programa.
while (true) con break
A veces el punto de salida natural está en mitad del cuerpo, después de algo de trabajo y antes del resto. Escribe el bucle como while (true) y sal con break:
Salida:
processing: resize photo
processing: send email
stop signal received
1 job(s) left in the queue
Dequeue saca el siguiente trabajo del principio de la cola, y el bucle sale en cuanto ve el marcador de parada, así que el último trabajo se queda en la cola. while (true) no es un mal olor de código cuando la salida es clara y está cerca del principio del cuerpo. Lo es cuando hay varios break repartidos por un cuerpo largo; entonces una condición bool con nombre o un método con return se siguen mejor. La página de break y continue explica cómo salir de bucles anidados.
Bucles que nunca terminan
Un bucle infinito no deseado tiene una de tres causas.
El cuerpo nunca cambia la condición. El contador no se incrementa nunca, o se incrementa la variable equivocada:
int i = 0;
while (i < 5)
{
Console.WriteLine(i); // i++ forgotten: prints 0 forever
}
Un punto y coma después de la condición. El punto y coma es todo el cuerpo del bucle, y el bloque de debajo se ejecuta después del bucle (que nunca termina):
while (retries < 3); // warning CS0642: Possible mistaken empty statement
{
retries++;
}
Una condición que se pasa de su objetivo. Probar != cuando la variable avanza a saltos puede saltarse el valor de parada. while (x != 10) x += 3; pasa por 0, 3, 6, 9, 12 y sigue de largo sin parar en 10 (solo cae en 10 por casualidad, después de que el int se desborde y dé la vuelta miles de millones de pasos más tarde). Prueba con < o > en lugar de != siempre que el paso no sea 1. Lo mismo vale para los valores double, que rara vez llegan a un objetivo exacto: while (x != 1.0) x += 0.1; nunca termina.
Elegir entre while, do while y for
| Situación | Bucle |
|---|---|
| Número conocido de iteraciones, o un índice | for |
| Cada elemento de una colección | foreach |
| Repetir mientras se cumpla una condición, quizá cero veces | while |
| El cuerpo debe ejecutarse una vez antes de que la condición tenga sentido | do while |
| Prueba de salida en mitad del cuerpo | while (true) con break |
Cualquier bucle for puede reescribirse como un bucle while y al revés; la elección depende de cuál pone la lógica del bucle donde el lector la espera. Cuando existen el contador, la prueba y el paso, for los mantiene en una línea. Cuando el bucle lo mueve un estado que cambia dentro del cuerpo, while lo dice.
Preguntas frecuentes
¿Qué diferencia hay entre while y do while en C#?
Un bucle while comprueba su condición antes de cada iteración, así que su cuerpo puede ejecutarse cero veces. Un bucle do { } while (condition); la comprueba después de cada iteración, así que su cuerpo siempre se ejecuta al menos una vez. Usa do while cuando la primera pasada produce el valor que prueba la condición, como leer una entrada y después validarla.
¿Cuál es la sintaxis de un bucle do while en C#?
do { body } while (condition);. Fíjate en el punto y coma después del paréntesis de cierre: es obligatorio, y omitirlo es un error de compilación. La condición puede usar variables asignadas en el cuerpo, pero deben declararse antes del do, porque las llaves del cuerpo son un ámbito aparte.
¿Cómo escribo while (true) en C#?
while (true) { ... } se repite para siempre hasta que algo dentro sale con break, return o una excepción. Es la forma natural cuando la prueba de salida va en mitad del cuerpo, como leer una línea y parar cuando está vacía.
¿Cómo leo la entrada hasta que el usuario escriba una palabra o se acabe la entrada en C#?
Haz el bucle sobre Console.ReadLine(), que devuelve null al final de la entrada: string line; while ((line = Console.ReadLine()) != null && line != "done") { ... }. La asignación dentro de la condición lee la línea siguiente y la prueba en un solo paso.
¿Por qué mi bucle while de C# nunca termina?
Normalmente el cuerpo nunca cambia lo que prueba la condición (un i++ olvidado), o hay un punto y coma justo después de la condición: while (count < 10); es un bucle con el cuerpo vacío que gira para siempre. El compilador avisa del segundo caso con CS0642, Possible mistaken empty statement.