Módulo 04: Estructuras de control

Objetivos del módulo

  • Entender qué son las estructuras de control y por qué existen
  • Dominar las estructuras condicionales: if, else if, else
  • Usar switch clásico y moderno (pattern matching)
  • Dominar los bucles: for, while, do-while, foreach
  • Controlar el flujo con break, continue y return
  • Anidar estructuras de control de forma legible

1. ¿Qué son las estructuras de control?

📘 Concepto: Las estructuras de control son instrucciones que permiten que tu programa tome decisiones (condicionales) o repita acciones (bucles). Sin ellas, el código se ejecutaría siempre de arriba a abajo, instrucción por instrucción, sin posibilidad de variar su comportamiento.

Flujo lineal (sin estructuras de control):
    instrucción 1
    instrucción 2
    instrucción 3
    instrucción 4
    (siempre igual)

Flujo con decisiones:
    instrucción 1
    ¿condición? ──→ Sí: instrucción 2a
                └──→ No: instrucción 2b
    instrucción 3

Flujo con bucles:
    instrucción 1
    ┌→ instrucción 2 ←┐
    │  ¿repetir? ─Sí──┘
    │         └──No──→ instrucción 3

2. Condicional if

La estructura más básica de decisión: “Si se cumple esta condición, ejecuta este código”.

int edad = 20;

if (edad >= 18)
{
    Console.WriteLine("Eres mayor de edad.");
    Console.WriteLine("Puedes votar.");
}

Explicación línea por línea:

Línea Código Explicación
if (edad >= 18) Evalúa si edad es mayor o igual a 18. El resultado es true o false.  
{ Inicio del bloque de código que se ejecutará si la condición es true.  
Console.WriteLine(...) Estas líneas solo se ejecutan si la condición es verdadera.  
} Fin del bloque. Si la condición es false, se salta todo el bloque.  

⚠️ Importante: La condición dentro del if siempre debe ser una expresión que devuelva bool (true o false). En C#, a diferencia de otros lenguajes, no puedes usar un número como condición (por ejemplo, if (1) no es válido).

Diagrama de flujo de if

    ┌──────────┐
    │  Inicio  │
    └────┬─────┘
         │
    ┌────▼─────┐
    │ ¿edad    │
    │ >= 18?   │
    └──┬───┬───┘
   Sí  │   │  No
       │   │
  ┌────▼───┐│
  │ Mostrar ││
  │ mensaje ││
  └────┬───┘│
       │    │
       └──┬─┘
    ┌─────▼────┐
    │   Fin    │
    └──────────┘

3. Condicional if-else

Permite ejecutar un bloque si la condición es verdadera y otro bloque diferente si es falsa.

int edad = 15;

if (edad >= 18)
{
    Console.WriteLine("Eres mayor de edad.");
}
else
{
    Console.WriteLine("Eres menor de edad.");
}
    ┌──────────────┐
    │ ¿edad >= 18? │
    └──┬───────┬───┘
   Sí  │       │  No
       │       │
  ┌────▼───┐  ┌▼──────────┐
  │ "Mayor │  │  "Menor   │
  │ de     │  │  de       │
  │ edad"  │  │  edad"    │
  └────┬───┘  └┬──────────┘
       │       │
       └───┬───┘
           ▼

4. Condicional else if

Para evaluar múltiples condiciones en cascada:

Console.Write("Introduce tu nota (0-10): ");
double nota = double.Parse(Console.ReadLine()!);

if (nota < 0 || nota > 10)
{
    Console.WriteLine("Nota inválida. Debe estar entre 0 y 10.");
}
else if (nota < 5)
{
    Console.WriteLine("SUSPENSO");
}
else if (nota < 6)
{
    Console.WriteLine("APROBADO");
}
else if (nota < 7)
{
    Console.WriteLine("BIEN");
}
else if (nota < 9)
{
    Console.WriteLine("NOTABLE");
}
else
{
    Console.WriteLine("SOBRESALIENTE");
}

Explicación:

Código Se ejecuta cuando…
nota < 0 \|\| nota > 10 La nota está fuera del rango válido
nota < 5 La nota es menor que 5 (y ya sabemos que está en rango porque la primera condición fue false)
nota < 6 La nota está entre 5 y 5.99
nota < 7 La nota está entre 6 y 6.99
nota < 9 La nota está entre 7 y 8.99
else Cualquier otro caso: nota entre 9 y 10

💡 Consejo: El ! después de Console.ReadLine()! es el operador de supresión de null. Le dice al compilador “confío en que esto no será null”. Es necesario porque ReadLine() podría devolver null.


5. Expresión switch clásica

switch es útil cuando quieres comparar una variable contra múltiples valores concretos:

Console.Write("Introduce el día de la semana (1-7): ");
int dia = int.Parse(Console.ReadLine()!);

switch (dia)
{
    case 1:
        Console.WriteLine("Lunes");
        break;
    case 2:
        Console.WriteLine("Martes");
        break;
    case 3:
        Console.WriteLine("Miércoles");
        break;
    case 4:
        Console.WriteLine("Jueves");
        break;
    case 5:
        Console.WriteLine("Viernes");
        break;
    case 6:
        Console.WriteLine("Sábado (fin de semana)");
        break;
    case 7:
        Console.WriteLine("Domingo (fin de semana)");
        break;
    default:
        Console.WriteLine("Día inválido");
        break;
}

Explicación:

Parte Significado
switch (dia) Evalúa la variable dia
case 1: Si dia vale 1, ejecuta el código de este bloque
break; Salir del switch. Obligatorio en C# (a diferencia de otros lenguajes)
default: Se ejecuta si ningún case coincide. Es como el else del switch

Agrupar casos

Si varios valores deben ejecutar el mismo código:

switch (dia)
{
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
        Console.WriteLine("Día laborable");
        break;
    case 6:
    case 7:
        Console.WriteLine("Fin de semana");
        break;
    default:
        Console.WriteLine("Día inválido");
        break;
}

6. Expresión switch moderna (C# 8+)

C# moderno permite usar switch como una expresión que devuelve un valor, de forma mucho más compacta:

int dia = 3;

string nombreDia = dia switch
{
    1 => "Lunes",
    2 => "Martes",
    3 => "Miércoles",
    4 => "Jueves",
    5 => "Viernes",
    6 => "Sábado",
    7 => "Domingo",
    _ => "Día inválido"   // _ es el caso por defecto (como default)
};

Console.WriteLine(nombreDia);  // "Miércoles"

Pattern matching con rangos

double nota = 7.5;

string calificacion = nota switch
{
    < 0 or > 10 => "Nota inválida",
    < 5 => "Suspenso",
    < 6 => "Aprobado",
    < 7 => "Bien",
    < 9 => "Notable",
    <= 10 => "Sobresaliente",
    _ => "Error"
};

Console.WriteLine($"Nota {nota}: {calificacion}");  // "Nota 7.5: Notable"

Pattern matching con tipos

object valor = 42;

string descripcion = valor switch
{
    int n when n > 0 => $"Número positivo: {n}",
    int n when n < 0 => $"Número negativo: {n}",
    int => "Cero",
    string s => $"Texto: {s}",
    bool b => $"Booleano: {b}",
    null => "Es null",
    _ => $"Tipo desconocido: {valor.GetType()}"
};

Console.WriteLine(descripcion);  // "Número positivo: 42"

7. Bucle while

Repite un bloque de código mientras una condición sea verdadera.

// Contar del 1 al 5
int contador = 1;

while (contador <= 5)
{
    Console.WriteLine($"Contador: {contador}");
    contador++;
}
Console.WriteLine("¡Fin del bucle!");

Traza de ejecución (paso a paso):

Iteración contador contador <= 5 Acción
1 1 true Imprime “Contador: 1”, incrementa a 2
2 2 true Imprime “Contador: 2”, incrementa a 3
3 3 true Imprime “Contador: 3”, incrementa a 4
4 4 true Imprime “Contador: 4”, incrementa a 5
5 5 true Imprime “Contador: 5”, incrementa a 6
6 6 false Sale del bucle

Diagrama:

    ┌──────────────────┐
    │ contador = 1     │
    └────────┬─────────┘
             │
    ┌────────▼─────────┐
    │ ¿contador <= 5?  │◄──────┐
    └──┬──────────┬────┘       │
   Sí  │          │ No         │
       │          │            │
  ┌────▼──────┐   │       ┌────┤
  │ Mostrar   │   │       │ ++ │
  │ contador  │───────────┘    │
  └───────────┘   │            │
                  │
            ┌─────▼───┐
            │  Fin     │
            └──────────┘

⚠️ Importante: Si la condición nunca se hace false, el bucle será infinito y tu programa se quedará colgado. Asegúrate siempre de que algo dentro del bucle haga que la condición eventualmente sea false.

Ejemplo práctico: Menú interactivo

string opcion = "";

while (opcion != "4")
{
    Console.WriteLine("\n═══ MENÚ ═══");
    Console.WriteLine("1. Saludar");
    Console.WriteLine("2. Mostrar fecha");
    Console.WriteLine("3. Mostrar hora");
    Console.WriteLine("4. Salir");
    Console.Write("Elige una opción: ");
    opcion = Console.ReadLine()!;

    switch (opcion)
    {
        case "1":
            Console.WriteLine("¡Hola, bienvenido!");
            break;
        case "2":
            Console.WriteLine($"Hoy es: {DateTime.Now:dd/MM/yyyy}");
            break;
        case "3":
            Console.WriteLine($"Son las: {DateTime.Now:HH:mm:ss}");
            break;
        case "4":
            Console.WriteLine("¡Hasta luego!");
            break;
        default:
            Console.WriteLine("Opción no válida.");
            break;
    }
}

8. Bucle do-while

Similar al while, pero ejecuta el bloque al menos una vez antes de comprobar la condición.

int numero;

do
{
    Console.Write("Introduce un número positivo: ");
    numero = int.Parse(Console.ReadLine()!);

    if (numero <= 0)
    {
        Console.WriteLine("¡Eso no es un número positivo! Inténtalo de nuevo.");
    }
} while (numero <= 0);

Console.WriteLine($"Has introducido: {numero}");

Diferencia clave:

while do-while
Comprueba la condición antes de ejecutar Ejecuta el código primero y luego comprueba
Puede no ejecutarse nunca Se ejecuta al menos una vez
while (cond) { código } do { código } while (cond);

💡 Consejo: Usa do-while cuando necesites que el código se ejecute al menos una vez, como al pedir datos al usuario (necesitas pedirlos antes de poder comprobar si son válidos).


9. Bucle for

El bucle for es ideal cuando sabes cuántas veces quieres repetir algo.

// Sintaxis: for (inicialización; condición; actualización)
for (int i = 0; i < 5; i++)
{
    Console.WriteLine($"Iteración {i}");
}

Partes del for:

Parte Código Significado
Inicialización int i = 0 Se ejecuta una sola vez al inicio. Crea la variable de control.
Condición i < 5 Se evalúa antes de cada iteración. Si es false, se sale del bucle.
Actualización i++ Se ejecuta al final de cada iteración.

Traza:

Paso i i < 5 Acción
1 0 true Imprime “Iteración 0”
2 1 true Imprime “Iteración 1”
3 2 true Imprime “Iteración 2”
4 3 true Imprime “Iteración 3”
5 4 true Imprime “Iteración 4”
6 5 false Sale del bucle

Variaciones del for

// Contar hacia atrás
for (int i = 10; i > 0; i--)
{
    Console.Write($"{i}... ");
}
Console.WriteLine("¡DESPEGUE!");

// De 2 en 2
for (int i = 0; i <= 20; i += 2)
{
    Console.Write($"{i} ");
}
// Salida: 0 2 4 6 8 10 12 14 16 18 20

// Tabla de multiplicar
int tabla = 7;
Console.WriteLine($"Tabla del {tabla}:");
for (int i = 1; i <= 10; i++)
{
    Console.WriteLine($"  {tabla} x {i} = {tabla * i}");
}

10. Bucle foreach

Recorre todos los elementos de una colección (array, lista, string, etc.) uno por uno, sin necesidad de usar un índice.

// foreach con un array
string[] frutas = { "Manzana", "Banana", "Cereza", "Dátil" };

foreach (string fruta in frutas)
{
    Console.WriteLine($"Fruta: {fruta}");
}

// foreach con un string (recorre carácter por carácter)
string palabra = "Hola";

foreach (char letra in palabra)
{
    Console.Write($"[{letra}] ");
}
// Salida: [H] [o] [l] [a]

📘 Concepto: foreach es ideal cuando quieres recorrer todos los elementos y no necesitas saber el índice. Para acceder por índice o modificar elementos, usa for.


11. Control de flujo: break y continue

break: sale del bucle inmediatamente

// Buscar el primer número divisible por 7 entre 1 y 100
for (int i = 1; i <= 100; i++)
{
    if (i % 7 == 0)
    {
        Console.WriteLine($"Primer múltiplo de 7: {i}");
        break;  // Sale del bucle
    }
}
// Salida: Primer múltiplo de 7: 7

continue: salta a la siguiente iteración

// Mostrar solo los números impares del 1 al 10
for (int i = 1; i <= 10; i++)
{
    if (i % 2 == 0)
    {
        continue;  // Salta los pares
    }
    Console.Write($"{i} ");
}
// Salida: 1 3 5 7 9

12. Bucles anidados

Un bucle dentro de otro bucle:

// Tablas de multiplicar del 1 al 5
for (int tabla = 1; tabla <= 5; tabla++)
{
    Console.WriteLine($"\n═══ Tabla del {tabla} ═══");
    for (int i = 1; i <= 10; i++)
    {
        Console.WriteLine($"  {tabla} x {i,2} = {tabla * i,3}");
    }
}

Ejemplo: Dibujar un rectángulo con asteriscos

Console.Write("Ancho: ");
int ancho = int.Parse(Console.ReadLine()!);
Console.Write("Alto: ");
int alto = int.Parse(Console.ReadLine()!);

for (int fila = 0; fila < alto; fila++)
{
    for (int columna = 0; columna < ancho; columna++)
    {
        Console.Write("* ");
    }
    Console.WriteLine();  // Salto de línea al final de cada fila
}

// Para ancho=5, alto=3:
// * * * * *
// * * * * *
// * * * * *

Ejemplo: Triángulo de asteriscos

int filas = 5;

for (int i = 1; i <= filas; i++)
{
    for (int j = 0; j < i; j++)
    {
        Console.Write("* ");
    }
    Console.WriteLine();
}

// Salida:
// *
// * *
// * * *
// * * * *
// * * * * *

13. Ejercicios

Ejercicio 1: Número par o impar

Pide un número al usuario y muestra si es par o impar.

Ejercicio 2: Máximo de tres números

Pide tres números al usuario y muestra cuál es el mayor.

Ejercicio 3: Adivina el número

El programa “piensa” un número aleatorio entre 1 y 100. El usuario tiene que adivinarlo. El programa dice “Más alto” o “Más bajo” tras cada intento:

Random random = new Random();
int secreto = random.Next(1, 101);  // Número entre 1 y 100
int intentos = 0;
int intento;

Console.WriteLine("He pensado un número entre 1 y 100. ¡Adivínalo!");

do
{
    Console.Write("Tu intento: ");
    intento = int.Parse(Console.ReadLine()!);
    intentos++;

    if (intento < secreto)
        Console.WriteLine("Más alto ↑");
    else if (intento > secreto)
        Console.WriteLine("Más bajo ↓");
    else
        Console.WriteLine($"¡Correcto! Lo adivinaste en {intentos} intentos.");

} while (intento != secreto);

Ejercicio 4: Factorial

Calcula el factorial de un número usando un bucle for:

  • 5! = 5 × 4 × 3 × 2 × 1 = 120

Ejercicio 5: Números primos

Pide un número al usuario y determina si es primo (solo divisible por 1 y por sí mismo).

Ejercicio 6: Pirámide de números

Crea esta pirámide pidiendo el número de filas:

    1
   1 2
  1 2 3
 1 2 3 4
1 2 3 4 5

Ejercicio 7: FizzBuzz

Imprime los números del 1 al 100, pero:

  • Si es múltiplo de 3, imprime “Fizz”
  • Si es múltiplo de 5, imprime “Buzz”
  • Si es múltiplo de ambos, imprime “FizzBuzz”

Solución:

for (int i = 1; i <= 100; i++)
{
    string resultado = (i % 3 == 0, i % 5 == 0) switch
    {
        (true, true) => "FizzBuzz",
        (true, false) => "Fizz",
        (false, true) => "Buzz",
        _ => i.ToString()
    };
    Console.WriteLine(resultado);
}

Resumen

Estructura Uso Ejemplo
if / else if / else Decisiones simples o en cascada if (x > 0) { ... }
switch (clásico) Comparar contra valores concretos switch (dia) { case 1: ... }
switch (expresión) Devolver un valor según una condición x switch { 1 => "uno", _ => "otro" }
while Repetir mientras la condición sea verdadera while (x < 10) { ... }
do-while Igual que while, pero ejecuta al menos una vez do { ... } while (x < 10);
for Repetir un número conocido de veces for (int i = 0; i < 10; i++)
foreach Recorrer todos los elementos de una colección foreach (var item in lista)
break Salir del bucle inmediatamente if (x == 5) break;
continue Saltar a la siguiente iteración if (x == 5) continue;