Menu

switch en C#: case, break, default y varios casos

Cómo funciona la sentencia switch en C#: switch sobre valores int, string, char y enum, por qué cada case necesita break (error CS0163), apilar etiquetas case para varios valores, goto case, default, la distinción entre mayúsculas y minúsculas en strings y las etiquetas case con patrones y when.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

Una sentencia switch compara un valor con una lista de etiquetas case constantes y ejecuta la sección que coincide. En C#, cada sección debe terminar con break (u otra sentencia que salga de ella), así que un case nunca puede pasar en silencio al siguiente.

Salida:

200: OK
302: redirect
404: not found
500: unhandled status

El valor entre paréntesis se evalúa una vez y se compara con cada etiqueta. default se ejecuta cuando nada coincide; es opcional y puede aparecer en cualquier lugar de la lista, aunque normalmente va al final. Si no hay coincidencia ni default, el switch entero no hace nada.

Cada sección necesita un break

En C, C++, Java y JavaScript, olvidar break hace que la ejecución se deslice al case siguiente. C# convierte ese error en un error de compilación:

switch (level)
{
    case 1:
        Console.WriteLine("read");
        // error CS0163: Control cannot fall through from one case label ('case 1:') to another
    case 2:
        Console.WriteLine("write");
        break;
}

La regla es que el final de la lista de sentencias de una sección no debe ser alcanzable. break es la forma habitual de cumplirla, pero sirve cualquier cosa que salga de la sección:

  • return sale del método entero, que suele ser la forma más limpia dentro de un método auxiliar pequeño.
  • throw lanza una excepción para un valor que nunca debería darse.
  • continue salta a la siguiente iteración cuando el switch está dentro de un bucle.
  • goto case y goto default saltan a otra sección a propósito.

La última sección no es una excepción. Un default: al final sin break falla con CS8070, Control cannot fall out of switch from final case label ('default:').

Varios valores en un mismo case

Las etiquetas sin sentencias entre ellas comparten la sección que tienen debajo. Es el único paso entre cases que C# permite, porque una etiqueta vacía no puede esconder código:

Salida:

'O' is a vowel
'k' is a consonant or symbol
' ' is a space
'7' is a digit

Aquí cada sección termina con return, así que no hace falta break (un break después de un return sería código inalcanzable). Dos etiquetas con el mismo valor son un error, CS0152 The switch statement contains multiple cases with the label value '1', y cada etiqueta debe ser una constante en tiempo de compilación: una variable en un case falla con "a constant value is expected".

switch sobre un string

Los strings son el valor más habitual en un switch después de los enteros. La comparación es ordinal y distingue mayúsculas, así que normaliza la entrada antes del switch:

Salida:

Starting the server
Stopping the server
Server is running
Unknown command: restart
Unknown command: (none)

Sin ToLowerInvariant, "STOP" llegaría a default. Hacer switch sobre un valor null es válido: no coincide con ninguna constante string y cae en default, así que el switch en sí nunca lanza una excepción.

switch sobre un enum

Los enums son para lo que se hizo switch: un conjunto cerrado de valores con nombre.

Salida:

Pending: wait for payment
Paid: pack and ship
Shipped: track the parcel
Delivered: nothing, order is closed
Cancelled: nothing, order is closed
ArgumentOutOfRangeException for value 42

El compilador no comprueba que una sentencia switch cubra todos los miembros del enum, y una variable enum puede contener cualquier entero de su tipo subyacente ((OrderStatus)42 es válido). El default que lanza una excepción convierte un case olvidado en un fallo inmediato y evidente en lugar de algo que no hace nada en silencio.

goto case y goto default

Cuando un case de verdad debe continuar en otro, C# te obliga a escribir el salto:

Salida:

admin:
  manage users
  edit posts
  read posts
editor:
  edit posts
  read posts
guest:
  read posts

goto case recibe una constante que debe coincidir con una de las etiquetas del mismo switch. Es raro en código real: el comportamiento compartido suele quedar más claro como un método al que llaman varios cases. Aun así, es la respuesta a "cómo paso de un case a otro en C#".

Variables dentro de los cases

Todo el bloque switch es un único ámbito, así que dos cases no pueden declarar una variable con el mismo nombre:

switch (shape)
{
    case "square":
        int area = side * side;
        break;
    case "rectangle":
        int area = width * height;   // error CS0128: A local variable or function named 'area' is already defined in this scope
        break;
}

Envuelve el cuerpo de cada sección en llaves para darle su propio ámbito: case "square": { int area = side * side; ... break; }.

break dentro de un bucle

break en un case sale del switch, no del bucle que lo rodea. continue hace lo contrario: un switch no es un bucle, así que continue pasa a la siguiente iteración del bucle.

Salida:

end marker found
Total: 134

El bucle siguió después de "end" y sumó 99. Para detener el bucle desde dentro de un switch, activa una variable indicadora y compruébala después del switch, o mueve el bucle a un método y usa return. La página de break y continue compara las opciones.

Etiquetas case con patrones y when

Desde C# 7.0, una etiqueta case puede ser un patrón en lugar de una constante: puede comprobar el tipo del valor y añadir una condición when. Esta forma es habitual en el código C# moderno:

static string Describe(object value)
{
    switch (value)
    {
        case null:
            return "nothing";
        case int n when n < 0:
            return $"negative int {n}";
        case int n:
            return $"int {n}";
        case string s when s.Length == 0:
            return "empty string";
        case string s:
            return $"string \"{s}\"";
        default:
            return $"other: {value.GetType().Name}";
    }
}

Con patrones, el orden importa: gana el primer case que coincide, y el compilador da un error cuando un case nunca puede alcanzarse porque uno anterior ya lo cubre (poner case int n: encima de case int n when n < 0: falla). La cláusula when también funciona en cases constantes: case 0 when isAdmin:. C# 9 añadió patrones relacionales y lógicos, así que un case puede ser case >= 90: o case 'a' or 'e' or 'i':.

En C# 7 sin patrones, la misma distribución por tipo es una cadena de if con el patrón de tipo is:

Salida:

negative int -3
int 12
empty string
string "hi"
other: Decimal
nothing

Cuando cada rama de un switch solo calcula un valor, la expresión switch es aún más corta: var grade = score switch { >= 90 => "A", >= 80 => "B", _ => "C" };. La página de la expresión switch explica su sintaxis, sus patrones y sus reglas de exhaustividad.

Sobre qué se puede hacer switch

Tipo del valorCases constantesNotas
int, long, byte y otros enterossíel caso clásico
charsícase 'a':
stringsíordinal, distingue mayúsculas, null va a default
boolsíválido, pero un if es más claro
cualquier enumsísin comprobación de exhaustividad
double, decimaldesde C# 7solo constantes; los rangos necesitan los patrones relacionales de C# 9
cualquier otro tipodesde C# 7mediante patrones de tipo, case Circle c:

Preguntas frecuentes

¿Es obligatorio break en un switch de C#?

Cada sección case debe terminar con una sentencia que salga de ella: normalmente break, pero también sirven return, throw, continue (dentro de un bucle) o goto case. Llegar al final de una sección y pasar a la siguiente es un error de compilación, CS0163 Control cannot fall through from one case label ('case 1:') to another, y eso incluye la última sección.

¿Cómo manejo varios valores en un mismo case de C#?

Apila las etiquetas sin nada entre ellas: case "sat": case "sun": Console.WriteLine("weekend"); break;. Las etiquetas case vacías pueden pasar a la siguiente etiqueta; solo una sección que contiene sentencias debe terminar con break. Desde C# 9, un case con patrón también puede escribirse case 1 or 2 or 3:.

¿Distingue mayúsculas y minúsculas un switch de C# sobre un string?

Sí. Los case de string se comparan con igualdad ordinal que distingue mayúsculas, así que "Yes" no coincide con case "yes":. Normaliza antes el valor con switch (input.Trim().ToLowerInvariant()). Un string null no coincide con ningún case constante y va a default.

¿Puede un switch de C# pasar de un case a otro como en C o Java?

No de forma implícita. Cuando quieras que un case continúe en otro, dilo con goto case <value>; o goto default;. Así cada paso de un case a otro queda visible en el código, y por eso C# prohíbe la versión silenciosa.

¿Qué diferencia hay entre una sentencia switch y una expresión switch en C#?

Una sentencia switch ejecuta sentencias y necesita case, break y default. Una expresión switch (C# 8 y posteriores) produce un valor: var label = n switch { 0 => "zero", > 0 => "positive", _ => "negative" };. Usa la expresión cuando cada rama solo calcula un resultado.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR