En esta página

Control de flujo y funciones en C# 14

14 min lectura TextoCap. 1 — Fundamentos de C#

Control de flujo en C#

El control de flujo determina el orden en que se ejecutan las instrucciones. C# ofrece las estructuras clásicas más expresivas actualizaciones modernas.

if / else

La estructura más básica para tomar decisiones:

int temperatura = 35;

if (temperatura > 40)
{
    Console.WriteLine("Calor extremo");
}
else if (temperatura > 30)
{
    Console.WriteLine("Calor fuerte");   // ← se ejecuta
}
else if (temperatura > 20)
{
    Console.WriteLine("Temperatura agradable");
}
else
{
    Console.WriteLine("Frío");
}

// If de una sola línea (sin llaves) — solo cuando sea obvio
if (temperatura > 0) Console.WriteLine("Sobre cero");

Expresión condicional (ternario)

bool esMayorDeEdad = edad >= 18;
string mensaje = esMayorDeEdad ? "Bienvenido" : "Acceso denegado";

// Ternario anidado (usa con moderación)
string nivel = puntos >= 90 ? "A" : puntos >= 70 ? "B" : puntos >= 50 ? "C" : "F";

switch statement

Para comparar un valor contra múltiples opciones:

string diaSemana = "Lunes";

switch (diaSemana)
{
    case "Lunes":
    case "Martes":
    case "Miércoles":
    case "Jueves":
    case "Viernes":
        Console.WriteLine("Día laboral");
        break;
    case "Sábado":
    case "Domingo":
        Console.WriteLine("Fin de semana");
        break;
    default:
        Console.WriteLine("Día inválido");
        break;
}

switch expressions (C# 8+, mejorado en C# 14)

La versión moderna y más expresiva. Devuelve un valor directamente:

// Sobre enums — el compilador verifica exhaustividad
enum Estado { Activo, Inactivo, Suspendido, Eliminado }

string DescribirEstado(Estado estado) => estado switch
{
    Estado.Activo     => "El usuario puede acceder",
    Estado.Inactivo   => "Cuenta desactivada temporalmente",
    Estado.Suspendido => "Cuenta suspendida por violación",
    Estado.Eliminado  => "Cuenta eliminada permanentemente"
    // Sin default: el compilador avisa si falta un caso del enum
};

// Con property patterns
record Pedido(decimal Total, string Pais, bool EsPremium);

string CalcularEnvio(Pedido p) => p switch
{
    { Total: > 100, Pais: "BO" }           => "Envío gratis",
    { EsPremium: true }                    => "Envío gratis (premium)",
    { Pais: "BO", Total: > 50 }            => "Envío con descuento",
    _                                      => "Envío estándar"
};

Bucles

for — cuando conoces el número de iteraciones

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

// Iteración inversa
for (int i = 10; i >= 1; i--)
{
    Console.Write($"{i} ");
}
Console.WriteLine();

// for con múltiples variables
for (int i = 0, j = 10; i < j; i++, j--)
{
    Console.WriteLine($"i={i}, j={j}");
}

foreach — para colecciones

string[] lenguajes = { "C#", "Go", "Rust", "Python", "TypeScript" };

foreach (string lang in lenguajes)
{
    Console.WriteLine($"  → {lang}");
}

// foreach con índice usando LINQ
foreach (var (lang, idx) in lenguajes.Select((l, i) => (l, i)))
{
    Console.WriteLine($"{idx + 1}. {lang}");
}

while — mientras se cumpla una condición

int intentos = 0;
int maxIntentos = 3;
bool exitoso = false;

while (intentos < maxIntentos && !exitoso)
{
    Console.Write("Ingresa la clave: ");
    string clave = Console.ReadLine() ?? "";
    intentos++;

    if (clave == "dotnet10")
    {
        exitoso = true;
        Console.WriteLine("¡Acceso concedido!");
    }
    else
    {
        Console.WriteLine($"Clave incorrecta. Intentos: {intentos}/{maxIntentos}");
    }
}

do-while — ejecuta al menos una vez

int numero;
do
{
    Console.Write("Ingresa un número positivo: ");
} while (!int.TryParse(Console.ReadLine(), out numero) || numero <= 0);

Console.WriteLine($"Número válido: {numero}");

break, continue y return

// break — sale del bucle
for (int i = 0; i < 100; i++)
{
    if (i == 5) break;
    Console.Write($"{i} "); // 0 1 2 3 4
}

// continue — salta a la siguiente iteración
for (int i = 0; i < 10; i++)
{
    if (i % 2 == 0) continue; // salta pares
    Console.Write($"{i} "); // 1 3 5 7 9
}

Métodos (funciones)

En C#, las funciones se llaman métodos y siempre pertenecen a una clase o struct. El punto de entrada Program.cs usa instrucciones de nivel superior, pero internamente también es un método.

// Método con tipo de retorno
static int Sumar(int a, int b)
{
    return a + b;
}

// Expression-bodied method (una sola expresión)
static int Multiplicar(int a, int b) => a * b;

// Método void (sin retorno)
static void Saludar(string nombre)
{
    Console.WriteLine($"¡Hola, {nombre}!");
}

// Parámetros opcionales (deben ir al final)
static string Formatear(string texto, bool mayusculas = false, char separador = ' ')
{
    string resultado = texto.Trim();
    if (mayusculas) resultado = resultado.ToUpper();
    return resultado.Replace(' ', separador);
}

Console.WriteLine(Formatear("hola mundo"));          // "hola mundo"
Console.WriteLine(Formatear("hola mundo", true));    // "HOLA MUNDO"
Console.WriteLine(Formatear("hola mundo", true, '-')); // "HOLA-MUNDO"

Named arguments

// Llamada con argumentos nombrados — mejora la legibilidad
string resultado = Formatear(
    texto: "hello world",
    separador: '_',
    mayusculas: true
);
Console.WriteLine(resultado); // "HELLO_WORLD"

Funciones locales

Las funciones locales son métodos definidos dentro de otro método. Solo son visibles dentro del método padre:

static string ProcesarPedido(string[] items, decimal descuento)
{
    // Función local — solo visible aquí
    decimal CalcularTotal()
    {
        decimal suma = 0;
        foreach (string item in items)
            suma += ObtenerPrecio(item);
        return suma * (1 - descuento);
    }

    decimal ObtenerPrecio(string item) => item switch
    {
        "laptop" => 999.99m,
        "mouse"  => 29.99m,
        "teclado" => 79.99m,
        _         => 0m
    };

    decimal total = CalcularTotal();
    return $"Total: ${total:F2}";
}

Práctica

  1. FizzBuzz: Usa un bucle for del 1 al 100. Si el número es divisible entre 3, imprime "Fizz"; entre 5, "Buzz"; entre ambos, "FizzBuzz"; de lo contrario el número.
  2. Calculadora con switch expression: Crea un método Calcular(double a, double b, string operacion) que use un switch expression para +, -, *, / y devuelva el resultado.
  3. params: Implementa un método Promedio(params double[] valores) que retorne el promedio de los valores recibidos.

En la siguiente lección aprenderemos a definir clases y objetos: el núcleo de la programación orientada a objetos en C#.

Prefiere switch expressions sobre switch statements
Los switch expressions son más concisos, fuerzan a cubrir todos los casos y pueden usarse donde se espera un valor (en asignaciones, return, etc.). El compilador advierte si faltan casos cuando el tipo es un enum.
Funciones locales vs lambdas
Usa funciones locales cuando necesites recursión, cuando el código sea complejo, o cuando quieras un nombre descriptivo. Usa lambdas cuando el código sea muy corto y se use una sola vez (por ejemplo, en LINQ).
ref y out en producción
Evita ref y out en APIs públicas — complican la firma del método y el código llamante. Úsalos en situaciones de performance crítica o cuando interactúes con APIs existentes que los requieran (como TryParse).
// Switch expression — C# 8+ modernizado en C# 14
string DiaSemana(int dia) => dia switch
{
    1 => "Lunes",
    2 => "Martes",
    3 => "Miércoles",
    4 => "Jueves",
    5 => "Viernes",
    6 => "Sábado",
    7 => "Domingo",
    _ => throw new ArgumentOutOfRangeException(nameof(dia), "Día inválido")
};

// Switch expression con guards (when)
string ClasificarEdad(int edad) => edad switch
{
    < 0             => throw new ArgumentException("Edad inválida"),
    < 13            => "Niño",
    >= 13 and < 18  => "Adolescente",
    >= 18 and < 65  => "Adulto",
    >= 65           => "Adulto mayor"
};

Console.WriteLine(DiaSemana(3));          // Miércoles
Console.WriteLine(ClasificarEdad(25));    // Adulto