Uma instrução switch compara um valor com uma lista de rótulos case constantes e executa a seção correspondente. Em C#, toda seção precisa terminar com break (ou outra instrução que saia dela), então um case nunca continua no próximo sem aviso.
Saída:
200: OK
302: redirect
404: not found
500: unhandled status
O valor entre parênteses é avaliado uma vez e comparado com cada rótulo. default executa quando nada corresponde; ele é opcional e pode aparecer em qualquer lugar da lista, embora normalmente venha por último. Se não houver correspondência nem default, o switch inteiro não faz nada.
Toda seção precisa de break
Em C, C++, Java e JavaScript, esquecer o break faz a execução escorregar para o próximo case. C# transforma esse erro em erro de compilação:
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;
}
A regra é que o fim da lista de instruções de uma seção não pode ser alcançável. break é a forma usual de cumprir isso, mas qualquer coisa que saia da seção funciona:
returnsai do método inteiro, o que muitas vezes é a forma mais limpa dentro de um método auxiliar pequeno.throwlança uma exceção para um valor que nunca deveria aparecer.continuepula para a próxima iteração quando oswitchestá dentro de um laço.goto caseegoto defaultpulam para outra seção de propósito.
A última seção não é exceção. Um default: no final sem break falha com CS8070, Control cannot fall out of switch from final case label ('default:').
Vários valores em um case
Rótulos sem instruções entre eles compartilham a seção abaixo. Esse é o único tipo de fall through que C# permite, porque um rótulo vazio não pode esconder código:
Saída:
'O' is a vowel
'k' is a consonant or symbol
' ' is a space
'7' is a digit
Cada seção aqui termina com return, então nenhum break é necessário (um break depois de um return seria código inalcançável). Dois rótulos com o mesmo valor são um erro, CS0152 The switch statement contains multiple cases with the label value '1', e todo rótulo precisa ser uma constante de tempo de compilação: uma variável em um case falha com "a constant value is expected".
switch com string
Strings são o valor de switch mais comum depois dos inteiros. A comparação é ordinal e diferencia maiúsculas de minúsculas, então normalize a entrada antes do switch:
Saída:
Starting the server
Stopping the server
Server is running
Unknown command: restart
Unknown command: (none)
Sem ToLowerInvariant, "STOP" chegaria ao default. Um valor null é permitido no switch: ele não corresponde a nenhuma constante string e cai no default, então o switch em si nunca lança exceção.
switch com enum
Enums são exatamente o caso para o qual o switch foi feito: um conjunto fechado de valores com nome.
Saída:
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
O compilador não verifica se uma instrução switch cobre todos os membros do enum, e uma variável enum pode guardar qualquer inteiro do tipo subjacente ((OrderStatus)42 é válido). O default que lança uma exceção transforma um case esquecido em uma falha imediata e óbvia, em vez de algo que silenciosamente não faz nada.
goto case e goto default
Quando um case realmente deve continuar em outro, C# obriga você a escrever o salto:
Saída:
admin:
manage users
edit posts
read posts
editor:
edit posts
read posts
guest:
read posts
goto case recebe uma constante que precisa corresponder a um dos rótulos do mesmo switch. É raro em código real: comportamento compartilhado costuma ficar mais claro como um método chamado por vários cases. Ainda assim, é a resposta para "como fazer fall through em C#".
Variáveis dentro dos cases
O bloco switch inteiro é um único escopo, então dois cases não podem declarar uma variável com o mesmo nome:
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;
}
Envolva o corpo de cada seção em chaves para dar a ela um escopo próprio: case "square": { int area = side * side; ... break; }.
break dentro de um laço
break em um case sai do switch, não do laço em volta. continue faz o contrário: um switch não é um laço, então continue vai para a próxima iteração do laço.
Saída:
end marker found
Total: 134
O laço continuou depois de "end" e somou 99. Para parar o laço de dentro de um switch, defina uma flag e teste-a depois do switch, ou mova o laço para um método e use return. A página de break e continue compara as opções.
Rótulos case com padrões e when
Desde o C# 7.0, um rótulo case pode ser um padrão em vez de uma constante: ele pode testar o tipo do valor e adicionar uma condição when. Essa forma é comum em 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}";
}
}
Com padrões, a ordem importa: o primeiro case que corresponde vence, e o compilador reporta um erro quando um case nunca pode ser alcançado porque um anterior já o cobre (colocar case int n: acima de case int n when n < 0: falha). A cláusula when também funciona em cases constantes: case 0 when isAdmin:. O C# 9 adicionou padrões relacionais e lógicos, então um case pode ser case >= 90: ou case 'a' or 'e' or 'i':.
No C# 7 sem padrões, o mesmo despacho por tipo é uma cadeia de if com o padrão de tipo is:
Saída:
negative int -3
int 12
empty string
string "hi"
other: Decimal
nothing
Quando cada ramo de um switch só calcula um valor, a expressão switch é ainda mais curta: var grade = score switch { >= 90 => "A", >= 80 => "B", _ => "C" };. A página da expressão switch cobre a sintaxe, os padrões e as regras de exaustividade.
Com o que dá para fazer switch
| Tipo do valor | Cases constantes | Observações |
|---|---|---|
int, long, byte e outros inteiros | sim | o caso clássico |
char | sim | case 'a': |
string | sim | ordinal, diferencia maiúsculas de minúsculas, null vai para default |
bool | sim | válido, mas um if é mais claro |
qualquer enum | sim | sem verificação de exaustividade |
double, decimal | a partir do C# 7 | só constantes; intervalos precisam dos padrões relacionais do C# 9 |
| qualquer outro tipo | a partir do C# 7 | por meio de padrões de tipo, case Circle c: |
Perguntas frequentes
O break é obrigatório em um switch em C#?
Toda seção case precisa terminar em uma instrução que saia dela: normalmente break, mas return, throw, continue (dentro de um laço) ou goto case também servem. Passar do fim de uma seção para a próxima é um erro de compilação, CS0163 Control cannot fall through from one case label ('case 1:') to another, e isso inclui a última seção.
Como tratar vários valores em um mesmo case de um switch em C#?
Empilhe os rótulos sem nada entre eles: case "sat": case "sun": Console.WriteLine("weekend"); break;. Rótulos case vazios podem passar para o próximo rótulo; só uma seção que contém instruções precisa terminar em break. A partir do C# 9, um case com padrão também pode escrever case 1 or 2 or 3:.
Um switch com string em C# diferencia maiúsculas de minúsculas?
Sim. Os cases de string são comparados por igualdade ordinal, que diferencia maiúsculas de minúsculas, então "Yes" não corresponde a case "yes":. Normalize o valor antes com switch (input.Trim().ToLowerInvariant()). Uma string null não corresponde a nenhum case constante e vai para default.
Um switch em C# pode fazer fall through como em C ou Java?
Não de forma implícita. Quando você quer que um case continue em outro, diga isso com goto case <value>; ou goto default;. Assim todo fall through fica visível no código, e é por isso que C# proíbe a versão silenciosa.
Qual a diferença entre a instrução switch e a expressão switch em C#?
Uma instrução switch executa instruções e precisa de case, break e default. Uma expressão switch (C# 8 em diante) produz um valor: var label = n switch { 0 => "zero", > 0 => "positive", _ => "negative" };. Use a expressão quando cada ramo só calcula um resultado.