En la lección anterior aprendiste a que tu programa tome decisiones con if/else, pero
esas decisiones se evaluaban una sola vez. En BiblioTechConsola, sin embargo, casi ninguna
tarea real consiste en mirar un único dato: hay que recorrer todo el catálogo de libros
para mostrarlo, contar cuántos ejemplares están disponibles, o buscar uno por título hasta
encontrarlo (o comprobar que no existe). Repetir una acción un número determinado o
indeterminado de veces es el trabajo de los bucles. En esta lección conocerás las cuatro
formas de bucle que ofrece C# —for, while, do-while y foreach— y aprenderás a
controlarlos con break y continue.
Contenido
- El bucle
for - El bucle
while - El bucle
do-while - El bucle
foreach - Comparativa: cuándo usar cada uno
breakycontinue- Bucles anidados
- Aplicación completa: listar, contar y buscar en el catálogo
- El bucle
for
forEl bucle for es ideal cuando sabes de antemano (o puedes calcular) cuántas veces se debe
repetir una acción, típicamente porque recorres una colección por índice.
string[] titulosLibros = { "Cien anos de soledad", "Rayuela", "Ficciones", "El Aleph" };
for (int i = 0; i < titulosLibros.Length; i++)
{
Console.WriteLine($"{i + 1}) {titulosLibros[i]}");
}La cabecera del for tiene tres partes separadas por ;:
| Parte | Ejemplo | Cuándo se ejecuta |
|---|---|---|
| Inicialización | int i = 0 |
Una sola vez, antes de empezar |
| Condición | i < titulosLibros.Length |
Antes de cada iteración; si es false, el bucle termina |
| Incremento (o actualización) | i++ |
Después de cada iteración |
Usar titulosLibros.Length en la condición, en lugar de un número fijo como 4, evita que el
bucle deje de funcionar (o falle) si el array cambia de tamaño más adelante.
También se puede recorrer en sentido inverso, algo útil cuando se van a eliminar elementos de
una colección mientras se recorre (tema que se ampliará con List<T> en el Módulo 4):
- El bucle
while
whilewhile repite un bloque mientras una condición sea true, evaluándola antes de cada
iteración. Es la opción natural cuando no se sabe de antemano cuántas veces hará falta repetir
la acción.
int indice = 0;
int librosDisponibles = 0;
bool[] disponibilidad = { true, false, true, true };
while (indice < disponibilidad.Length)
{
if (disponibilidad[indice])
{
librosDisponibles++;
}
indice++;
}
Console.WriteLine($"Libros disponibles: {librosDisponibles}");Un caso típico de while es esperar una entrada válida del usuario, donde no se sabe cuántos
intentos hará falta:
string? entrada = null;
int paginasLibro = 0;
bool entradaValida = false;
while (!entradaValida)
{
Console.Write("Introduce el numero de paginas del libro: ");
entrada = Console.ReadLine();
entradaValida = int.TryParse(entrada, out paginasLibro);
if (!entradaValida)
{
Console.WriteLine("Entrada no valida, intentalo de nuevo.");
}
}
Console.WriteLine($"Numero de paginas registrado: {paginasLibro}");Cuidado: si la condición de un while nunca llega a ser false, el bucle se ejecuta para
siempre (un bucle infinito). Siempre debe existir, dentro del bloque, alguna instrucción
que acerque la condición a su fin (en el primer ejemplo, indice++).
- El bucle
do-while
do-whiledo-while es casi idéntico a while, con una diferencia clave: la condición se evalúa
después de ejecutar el bloque, así que el bloque se ejecuta siempre al menos una vez,
incluso si la condición ya era false desde el principio.
int intentos = 0;
bool prestamoRegistrado = false;
do
{
intentos++;
Console.WriteLine($"Intento numero {intentos} de registrar el prestamo...");
// Simulamos que el registro tiene exito en el segundo intento
prestamoRegistrado = intentos >= 2;
} while (!prestamoRegistrado);
Console.WriteLine("Prestamo registrado correctamente.");Nota la sintaxis: la palabra while y su condición van después de la llave de cierre, y
la línea termina con ;. Este patrón encaja bien con menús interactivos, donde el menú debe
mostrarse al menos una vez antes de comprobar si el usuario quiere salir.
- El bucle
foreach
foreachforeach recorre todos los elementos de una colección (un array, o cualquier tipo que sea
"iterable", como verás con más detalle en el Módulo 4 con las colecciones), sin necesidad de
gestionar manualmente un índice ni una condición de parada.
string[] titulosLibros = { "Cien anos de soledad", "Rayuela", "Ficciones", "El Aleph" };
foreach (string titulo in titulosLibros)
{
Console.WriteLine(titulo);
}foreach es más legible que for cuando no necesitas el índice, pero tiene una limitación
importante: la variable de iteración (titulo en el ejemplo) es de solo lectura dentro
del bucle; no puedes usar foreach para modificar los elementos del array directamente a
través de ella.
int[] copiasDisponibles = { 2, 0, 1, 3 };
// foreach (int copias in copiasDisponibles)
// {
// copias++; // Error de compilacion: no se puede asignar a la variable de iteracion
// }
// Si necesitas modificar el array, usa for con indice:
for (int i = 0; i < copiasDisponibles.Length; i++)
{
copiasDisponibles[i]++;
}
- Comparativa: cuándo usar cada uno
| Bucle | Se usa cuando... | Evalúa la condición... |
|---|---|---|
for |
Sabes (o puedes calcular) el número de iteraciones, o necesitas el índice | Antes de cada iteración |
while |
No sabes cuántas iteraciones harán falta, y podría ser cero | Antes de cada iteración |
do-while |
Como while, pero el bloque debe ejecutarse al menos una vez |
Después de cada iteración |
foreach |
Solo necesitas recorrer todos los elementos, sin índice ni modificarlos | Antes de cada iteración (de forma interna) |
En la práctica, for y foreach cubren la inmensa mayoría de los recorridos sobre arrays y
colecciones en BiblioTechConsola; while y do-while se reservan para situaciones donde la
condición de parada depende de algo distinto a "recorrer una colección completa", como
validar una entrada de usuario o esperar a que se cumpla una condición externa.
break y continue
break y continueDentro de cualquier bucle, dos palabras clave permiten alterar el flujo normal de iteración:
break: termina el bucle inmediatamente, sin evaluar más iteraciones.continue: salta el resto del cuerpo del bucle en la iteración actual y pasa directamente a la siguiente.
string[] titulosLibros = { "Cien anos de soledad", "Rayuela", "Ficciones", "El Aleph" };
string tituloBuscado = "Ficciones";
for (int i = 0; i < titulosLibros.Length; i++)
{
if (titulosLibros[i] == tituloBuscado)
{
Console.WriteLine($"Encontrado en la posicion {i}.");
break; // ya no hace falta seguir recorriendo el array
}
}bool[] disponibilidad = { true, false, true, true, false };
for (int i = 0; i < disponibilidad.Length; i++)
{
if (!disponibilidad[i])
{
continue; // saltamos los libros no disponibles
}
Console.WriteLine($"Libro en posicion {i}: disponible");
}break también funciona dentro de while, do-while y foreach; continue, igualmente.
- Bucles anidados
Un bucle puede contener otro bucle en su interior. Esto es habitual al trabajar con arrays de dos dimensiones (que ya viste en la lección de arrays), por ejemplo, para representar un mapa de estanterías y baldas en BiblioTech:
string[,] mapaEstanterias = new string[2, 3]
{
{ "Novela", "Novela", "Ensayo" },
{ "Poesia", "Infantil", "Infantil" }
};
for (int estanteria = 0; estanteria < 2; estanteria++)
{
for (int balda = 0; balda < 3; balda++)
{
Console.WriteLine($"Estanteria {estanteria}, balda {balda}: {mapaEstanterias[estanteria, balda]}");
}
}El bucle exterior recorre las estanterías; por cada estantería, el bucle interior recorre sus
baldas. En total se ejecutan 2 × 3 = 6 iteraciones. Ten en cuenta que break y continue
solo afectan al bucle más interno en el que se encuentran; si necesitas salir de ambos
bucles a la vez, una técnica sencilla es usar una variable booleana como bandera:
bool encontrado = false;
for (int estanteria = 0; estanteria < 2 && !encontrado; estanteria++)
{
for (int balda = 0; balda < 3; balda++)
{
if (mapaEstanterias[estanteria, balda] == "Ensayo")
{
Console.WriteLine($"Genero encontrado en estanteria {estanteria}, balda {balda}");
encontrado = true;
break;
}
}
}
- Aplicación completa: listar, contar y buscar en el catálogo
Vamos a combinar todo lo anterior en tres operaciones típicas sobre el catálogo de BiblioTechConsola: listar los títulos, contar cuántos hay disponibles y buscar uno por título.
string[] titulosLibros = { "Cien anos de soledad", "Rayuela", "Ficciones", "El Aleph", "Pedro Paramo" };
bool[] disponibilidad = { true, false, true, true, false };
// 1) Listar todos los titulos con su estado (foreach no sirve aqui porque necesitamos el indice)
Console.WriteLine("=== Catalogo BiblioTech ===");
for (int i = 0; i < titulosLibros.Length; i++)
{
string estado = disponibilidad[i] ? "Disponible" : "Prestado";
Console.WriteLine($"{i + 1}) {titulosLibros[i]} - {estado}");
}
// 2) Contar cuantos libros estan disponibles (foreach: no necesitamos el indice)
int totalDisponibles = 0;
foreach (bool estaDisponible in disponibilidad)
{
if (estaDisponible)
{
totalDisponibles++;
}
}
Console.WriteLine($"Total de libros disponibles: {totalDisponibles}");
// 3) Buscar un titulo concreto y detenerse en cuanto se encuentra
string tituloBuscado = "Ficciones";
bool encontrado = false;
for (int i = 0; i < titulosLibros.Length; i++)
{
if (titulosLibros[i] == tituloBuscado)
{
Console.WriteLine($"'{tituloBuscado}' encontrado en la posicion {i + 1}, disponible: {disponibilidad[i]}");
encontrado = true;
break;
}
}
if (!encontrado)
{
Console.WriteLine($"'{tituloBuscado}' no esta en el catalogo.");
}Este fragmento ya combina un for (cuando el índice importa), un foreach (cuando solo
importa el valor), y break (para detener una búsqueda en cuanto se encuentra el resultado):
tres decisiones deliberadas, no arbitrarias.
Errores Comunes y Consejos
- Bucles infinitos: olvidar actualizar la variable de control en un
while(por ejemplo, no incrementar el índice) deja el programa colgado para siempre. Revisa siempre que la condición pueda llegar a serfalse. - Desbordar el índice de un array: en un
for, usar<=en lugar de<contraarray.Lengthprovoca acceder a una posición que no existe y lanza una excepción en tiempo de ejecución (lo verás en detalle en la última lección del módulo). - Intentar modificar la variable de
foreach: como se ha visto, no compila. Si necesitas modificar los elementos, usaforcon índice. - Usar
forcuandoforeaches más claro (y viceversa): si no necesitas el índice, prefiereforeach; mejora la legibilidad y reduce el riesgo de errores de índice. - Confundir
breakconcontinue:breaksale del bucle entero;continuesolo salta a la siguiente iteración. Un uso equivocado suele producir resultados incompletos o bucles que terminan antes de tiempo. - Anidar bucles sin necesidad: antes de anidar dos bucles, comprueba si el problema puede resolverse con un único recorrido; el anidamiento multiplica el número de iteraciones (y el tiempo de ejecución) y añade complejidad.
Ejercicios
-
Dado el array
int[] copiasDisponibles = { 3, 0, 5, 2, 0 };, usa un bucleforpara recorrerlo y mostrar por consola cuántas posiciones tienen0copias disponibles (libros agotados). -
Usa un bucle
whilepara simular la validación de una contraseña de administrador: parte destring intento = "";y de una lista simulada de intentos válidosstring[] intentosSimulados = { "1234", "abcd", "bibliotech" };(recorre este array con un índice creciente en cada vuelta delwhile, como si fueran intentos sucesivos del usuario). El bucle debe terminar en cuantointentosea igual a"bibliotech", mostrando cuántos intentos hicieron falta. -
Dado el array de títulos
string[] titulosLibros = { "Cien anos de soledad", "Rayuela", "Ficciones", "El Aleph" };, recorre el array conforeachy, usandocontinue, muestra por consola únicamente los títulos que tengan más de 8 caracteres.
Soluciones
int[] copiasDisponibles = { 3, 0, 5, 2, 0 };
int librosAgotados = 0;
for (int i = 0; i < copiasDisponibles.Length; i++)
{
if (copiasDisponibles[i] == 0)
{
librosAgotados++;
}
}
Console.WriteLine($"Libros agotados: {librosAgotados}");
El resultado es 2, correspondiente a las posiciones 1 y 4 (índices empezando en 0).
string[] intentosSimulados = { "1234", "abcd", "bibliotech" };
string intento = "";
int indice = 0;
int numeroIntentos = 0;
while (intento != "bibliotech")
{
intento = intentosSimulados[indice];
numeroIntentos++;
indice++;
}
Console.WriteLine($"Contrasena correcta encontrada tras {numeroIntentos} intento(s).");
El resultado muestra 3 intentos, porque "bibliotech" ocupa la tercera posición del
array simulado (índice 2). Fíjate en que la condición del while se evalúa antes de cada
vuelta, así que la última comprobación (intento != "bibliotech", ya false) es la que
detiene el bucle sin ejecutar una vuelta de más.
string[] titulosLibros = { "Cien anos de soledad", "Rayuela", "Ficciones", "El Aleph" };
foreach (string titulo in titulosLibros)
{
if (titulo.Length <= 8)
{
continue;
}
Console.WriteLine(titulo);
}
Se muestran "Cien anos de soledad" (21 caracteres) y "Ficciones" (9 caracteres), ambos
con más de 8 caracteres. "Rayuela" (7 caracteres) y "El Aleph" (8 caracteres exactos)
se omiten porque su longitud es menor o igual que 8: el continue salta esas dos
iteraciones sin mostrar nada. Es un buen ejercicio para comprobar con
Console.WriteLine(titulo.Length) cómo cambia el resultado si usas < en lugar de <=.
Conclusión
En esta lección has aprendido a repetir acciones con for, while y do-while, a recorrer
colecciones de forma sencilla con foreach, a controlar el flujo de un bucle con break y
continue, y a combinar bucles anidados cuando los datos tienen más de una dimensión. Con
estas herramientas, BiblioTechConsola ya puede listar su catálogo completo, contar libros
disponibles y buscar títulos concretos sin repetir código a mano.
Hasta ahora, para clasificar algo (como el estado de un préstamo) has usado cadenas de
if/else if/else. En la siguiente lección, Sentencias Switch, conocerás una
alternativa más clara cuando hay que comparar una misma variable contra muchos valores
posibles, por ejemplo, para clasificar un libro según su categoría o género dentro de
BiblioTech.
Curso de Programación en C#
Módulo 1: Introducción a C#
- Introducción a C#
- Configuración del Entorno de Desarrollo
- Programa Hola Mundo
- Sintaxis y Estructura Básica
- Variables y Tipos de Datos
- Arrays y Cadenas de Texto
Módulo 2: Estructuras de Control
Módulo 3: Programación Orientada a Objetos
- Clases y Objetos
- Métodos
- Constructores y Destructores
- Herencia
- Polimorfismo
- Encapsulamiento
- Abstracción
- Structs y Records: Tipos por Valor y por Referencia
Módulo 4: Conceptos Avanzados de C#
- Interfaces
- Delegados y Eventos
- Pattern Matching y Características Modernas de C#
- Genéricos
- Colecciones
- LINQ (Consulta Integrada en el Lenguaje)
- Programación Asíncrona
Módulo 5: Trabajando con Datos
- Entrada/Salida de Archivos
- Serialización
- Conectividad con Bases de Datos
- Entity Framework
- Trabajo con JSON y Consumo de APIs REST
Módulo 6: Temas Avanzados
- Reflexión
- Atributos
- Programación Dinámica
- Gestión de Memoria y Recolección de Basura
- Multihilo y Programación Paralela
Módulo 7: Construcción de Aplicaciones
- Formularios de Windows
- WPF (Windows Presentation Foundation)
- ASP.NET Core
- Blazor
- Xamarin y .NET MAUI
Módulo 8: Mejores Prácticas y Patrones de Diseño
- Estándares de Codificación y Mejores Prácticas
- Patrones de Diseño
- Inyección de Dependencias e Inversión de Control
- Pruebas Unitarias
- Revisión y Refactorización de Código
