C# switch

Quando um programa precisa escolher entre muitas opções olhando para uma mesma variável (a opção de um menu, o dia da semana, a forma de pagamento), uma cadeia de else if repete opcao == … em toda linha. O switch ("chave", como a chave seletora de um aparelho) faz isso de forma mais organizada: você diz qual variável olhar e lista os valores possíveis, cada um com o seu bloco. E o C# tem uma regra própria, diferente do C e do Java, que evita um dos erros mais comuns dessas linguagens.

Você vai aprender
  • As partes do switch: case, break e default.
  • Por que o C# não deixa um case cair no seguinte (erros CS0163 e CS8070).
  • Como juntar vários valores no mesmo bloco, e o case 1 or 7: C# 9.
  • switch com texto (string), e por que "PIX" não é "pix".
  • Variáveis dentro de um case (CS0128), case repetido (CS0152) e case com variável (CS9135).

Todos os programas desta aula rodaram com o .NET 10 (C# 14) na máquina fictícia da Ana, na pasta ~/cs, com dotnet run arquivo.cs.


A ideia: o menu do caixa eletrônico

No caixa eletrônico, a tela mostra: "1 - Ver saldo, 2 - Fazer um Pix, 3 - Sair". Você aperta uma tecla, e a máquina vai direto para a tela daquela opção. Ela não pergunta "é 1? não. é 2? não. é 3?" como quem lê uma lista: ela olha o número e pula para o lugar certo. Se você apertar 9, ela mostra "opção inválida".

O switch é esse menu. Cada opção é um case ("caso"), e o "opção inválida" é o default ("padrão").


1. O primeiro switch

int opcao = 2;
switch (opcao)
{
    case 1:
        Console.WriteLine("Ver saldo");
        break;
    case 2:
        Console.WriteLine("Fazer um Pix");
        break;
    case 3:
        Console.WriteLine("Sair");
        break;
    default:
        Console.WriteLine("Opção inválida");
        break;
}
Console.WriteLine("Obrigado por usar o banco da Ana.");
$ dotnet run menu.cs
Fazer um Pix                          « opcao vale 2: pulou para o case 2
Obrigado por usar o banco da Ana.

Troque o 2 por 9 e rode de novo:

$ dotnet run menu.cs
Opção inválida                        « nenhum case é 9: foi para o default
Obrigado por usar o banco da Ana.
switch (opcao)
A variável (ou expressão) que vai ser olhada, entre parênteses. Ela é calculada uma vez só.
{ … }
As chaves envolvem todos os casos. Elas são obrigatórias no switch.
case 1:
"No caso de valer 1": um rótulo. Termina com dois-pontos, não com ponto e vírgula. O valor tem de ser uma constante (um número, um texto entre aspas, uma const).
Console.WriteLine(…);
As instruções daquele caso. Pode haver quantas você quiser, sem chaves.
break;
"Quebre": sai do switch e continua depois da chave de fechamento. Em C#, todo caso precisa terminar com um break (ou outra instrução que saia dali; veja a seção 2).
default:
O caso "nenhum dos anteriores". É opcional e também termina com break. Costuma ficar no fim.

Sem default, se nenhum case combinar, o switch simplesmente não faz nada e o programa continua.


2. O C# não deixa um case "cair" no próximo

Em C, C++, Java e JavaScript, esquecer o break faz o programa continuar no case de baixo (em inglês, fall-through, "cair através"). É um erro tão comum, e tão difícil de enxergar, que o C# resolveu proibir. A Ana esqueceu o break do case 1:

int opcao = 1;
switch (opcao)
{
    case 1:
        Console.WriteLine("Ver saldo");
    case 2:
        Console.WriteLine("Fazer um Pix");
        break;
}
$ dotnet run cair.cs
/home/ana/cs/cair.cs(4,5): error CS0163: Controle não pode passar através de um rótulo case ("case 1:") para outro

Ocorreu uma falha no build. Corrija os erros de build e execute novamente.

"O controle não pode passar de um rótulo case (o case 1:) para outro" (em inglês, Control cannot fall through from one case label ('case 1:') to another). O (4,5) aponta a linha do case 1:. Em C ou Java, esse programa compilaria e, com a opção 1, mostraria "Ver saldo" e "Fazer um Pix": um Pix que ninguém pediu. Em C#, ele nem compila.

O último caso também precisa do break, mesmo sem ninguém embaixo para "cair":

int opcao = 1;
switch (opcao)
{
    case 1:
        Console.WriteLine("Ver saldo");
        break;
    default:
        Console.WriteLine("Opção inválida");
}
$ dotnet run ultimo.cs
/home/ana/cs/ultimo.cs(7,5): error CS8070: O controle não pode ficar fora do switch do rótulo de caso final ('default:')

Ocorreu uma falha no build. Corrija os erros de build e execute novamente.

"O controle não pode sair do switch pelo fim do último caso" (Control cannot fall out of switch from final case label ('default:')). A regra é a mesma para todos: todo caso termina com um comando que sai dele. Os comandos que servem são:

ComandoO que fazOnde aprender
break;Sai do switch. É o comum.Esta aula
goto case X;Pula para outro caso, de propósito (seção 6).Esta aula
return;Sai do método inteiro.Valor de Retorno
throw …;Lança uma exceção.throw
continue;Dentro de um laço, vai para a próxima volta.break e continue

Missão: este programa não compila (rode e veja o CS0163). Conserte-o para que, com a opção 1, ele mostre só o saldo e o agradecimento:

Ver saldo
Obrigado!
int opcao = 1;
switch (opcao)
{
    case 1:
        Console.WriteLine("Ver saldo");
    case 2:
        Console.WriteLine("Fazer um Pix");
        break;
    case 3:
        Console.WriteLine("Sair");
        break;
}
Console.WriteLine("Obrigado!");
int opcao = 1;
switch (opcao)
{
    case 1:
        Console.WriteLine("Ver saldo");
        break;
    case 2:
        Console.WriteLine("Fazer um Pix");
        break;
    case 3:
        Console.WriteLine("Sair");
        break;
}
Console.WriteLine("Obrigado!");

3. Vários valores, o mesmo bloco

A regra do break vale para casos que têm instruções. Vários rótulos colados, sem nada entre eles, são permitidos: é o jeito de dizer "para 1 ou 7, faça isto". A lojinha da Ana abre mais tarde no fim de semana (1 = domingo, 7 = sábado):

int dia = 7;
switch (dia)
{
    case 1:
    case 7:
        Console.WriteLine("Fim de semana: a loja abre às 10h.");
        break;
    case 2:
    case 3:
    case 4:
    case 5:
    case 6:
        Console.WriteLine("Dia útil: a loja abre às 8h.");
        break;
    default:
        Console.WriteLine("Dia inválido.");
        break;
}
$ dotnet run dias.cs
Fim de semana: a loja abre às 10h.

O case 1: não tem nenhuma instrução, então não há nada para "cair": os dois rótulos apontam para o mesmo bloco.

O jeito moderno: or e comparações no case C# 9

Desde o C# 9, o case aceita padrões: or ("ou") junta valores, and ("e") junta condições, e >=, < etc. comparam. O mesmo programa fica mais curto:

int dia = 1;
switch (dia)
{
    case 1 or 7:
        Console.WriteLine("Fim de semana: a loja abre às 10h.");
        break;
    case >= 2 and <= 6:
        Console.WriteLine("Dia útil: a loja abre às 8h.");
        break;
    default:
        Console.WriteLine("Dia inválido.");
        break;
}
$ dotnet run dias2.cs
Fim de semana: a loja abre às 10h.

Com comparações, o switch também resolve faixas, como uma cadeia de else if. Os casos são testados de cima para baixo, então a ordem importa do mesmo jeito:

int nota = 85;
switch (nota)
{
    case >= 90:
        Console.WriteLine("Excelente");
        break;
    case >= 60:
        Console.WriteLine("Aprovada");
        break;
    default:
        Console.WriteLine("Reprovada");
        break;
}
$ dotnet run relacional.cs
Aprovada

Esses padrões aparecem muito mais na expressão switch, a próxima aula, e são aprofundados em Pattern Matching.


4. switch com texto

Em C#, o switch funciona com string também (em C, não dá). A Ana fez o caixa da lojinha escolher pela forma de pagamento:

string forma = "pix";
switch (forma)
{
    case "pix":
        Console.WriteLine("Mostrando o QR Code...");
        break;
    case "cartão":
        Console.WriteLine("Aproxime o cartão.");
        break;
    case "dinheiro":
        Console.WriteLine("Abra o caixa.");
        break;
    default:
        Console.WriteLine($"Forma de pagamento desconhecida: {forma}");
        break;
}
$ dotnet run pagamento.cs
Mostrando o QR Code...

Agora troque "pix" por "PIX" na primeira linha:

$ dotnet run pagamento.cs
Forma de pagamento desconhecida: PIX

A comparação é exata: maiúsculas e minúsculas contam, acentos contam ("cartao" não é "cartão"), espaços contam. Quando o texto vem de uma pessoa digitando, é comum padronizar antes: switch (forma.ToLower()) transforma tudo em minúsculas (aula Métodos de String). O default mostrando o valor recebido, como aqui, ajuda muito a descobrir esse tipo de problema.


5. Variáveis dentro de um case

Todos os casos de um switch ficam dentro do mesmo par de chaves. Por isso, duas variáveis com o mesmo nome em casos diferentes brigam:

int opcao = 2;
switch (opcao)
{
    case 1:
        string msg = "Ver saldo";
        Console.WriteLine(msg);
        break;
    case 2:
        string msg = "Fazer um Pix";
        Console.WriteLine(msg);
        break;
}
$ dotnet run escopocase.cs
/home/ana/cs/escopocase.cs(9,16): error CS0128: Uma variável de local ou função denominada 'msg' já está definida neste escopo
/home/ana/cs/escopocase.cs(10,27): error CS0165: Uso de variável local não atribuída "msg"
/home/ana/cs/escopocase.cs(9,16): warning CS0219: A variável "msg" é atribuída, mas seu valor nunca é usado

Ocorreu uma falha no build. Corrija os erros de build e execute novamente.

O primeiro erro é o que importa: "já existe uma variável chamada msg neste escopo" (linha 9). Os outros dois são o compilador confuso com as duas msg. O conserto é dar a cada caso as suas próprias chaves, que criam um bloco separado:

int opcao = 2;
switch (opcao)
{
    case 1:
    {
        string msg = "Ver saldo";
        Console.WriteLine(msg);
        break;
    }
    case 2:
    {
        string msg = "Fazer um Pix";
        Console.WriteLine(msg);
        break;
    }
}
$ dotnet run escopocase2.cs
Fazer um Pix

(Outra saída: usar nomes diferentes, ou declarar a variável uma vez só, antes do switch.)


6. Passar para outro caso de propósito: goto case

E se você quiser que um caso continue no outro? O C# exige que você diga isso com todas as letras, com goto case ("vá para o caso"). Na pizzaria da Ana, a pizza P custa R$ 20, a M custa R$ 3 a mais que a P, e a G, R$ 5 a mais que a M:

string tamanho = "G";
decimal preco = 0m;
switch (tamanho)
{
    case "G":
        preco += 5m;
        goto case "M";
    case "M":
        preco += 3m;
        goto case "P";
    case "P":
        preco += 20m;
        break;
}
Console.WriteLine($"Pizza {tamanho}: R$ {preco:N2}");
$ dotnet run gotocase.cs
Pizza G: R$ 28,00            « 5 (G) + 3 (M) + 20 (P)

Funciona, mas é raro em código de verdade: quase sempre existe um jeito mais simples (aqui, uma conta direta em cada caso). O importante é saber que, em C#, nenhum caso continua no outro por acidente.


switch ou else if?

SituaçãoUse
Uma variável comparada com vários valores exatos (opção 1, 2, 3; "pix", "cartão")switch
Condições diferentes em cada caminho (idade < 12, depois temChuva && temFrio…)if / else if
Faixas de uma mesma variávelOs dois servem; o switch com case >= 90: fica mais limpo quando são muitas
Escolher um valor (não uma ação) para cada casoExpressão switch

Quando dá errado

error CS0163: Controle não pode passar através de um rótulo case ("case 1:") para outro

O que significa: um caso com instruções terminou sem break, e ia continuar no caso seguinte. O C# proíbe isso.

Como consertar: ponha break; no fim do caso apontado. Se você queria mesmo continuar no outro, use goto case X;; se queria que os dois valores fizessem a mesma coisa, cole os rótulos (case 1: case 2:) ou use case 1 or 2:.

error CS8070: O controle não pode ficar fora do switch do rótulo de caso final ('default:')

O que significa: o último caso (aqui, o default) ficou sem break.

Como consertar: break; no fim dele também.

error CS0152: A instrução switch contém vários casos com o valor de rótulo "1"
$ dotnet run duplicado.cs
/home/ana/cs/duplicado.cs(7,5): error CS0152: A instrução switch contém vários casos com o valor de rótulo "1"

Ocorreu uma falha no build. Corrija os erros de build e execute novamente.

O que significa: há dois case 1: no mesmo switch (normalmente, você copiou um caso e esqueceu de trocar o valor).

Como consertar: troque o valor do segundo (linha 7), ou junte os dois.

error CS9135: Um valor constante do tipo 'int' é esperado
$ cat casevar.cs
int opcao = 1;
int sair = 3;
switch (opcao)
{
    case sair:
        Console.WriteLine("Sair");
        break;
}
$ dotnet run casevar.cs
/home/ana/cs/casevar.cs(5,10): error CS9135: Um valor constante do tipo 'int' é esperado

Ocorreu uma falha no build. Corrija os erros de build e execute novamente.

O que significa: o valor de um case tem de ser conhecido antes de o programa rodar. Uma variável comum pode mudar, então não serve.

Como consertar: use uma constante (aula Constantes): const int Sair = 3; e case Sair: funcionam. Se o valor muda mesmo, use if.

error CS0029: Não é possível converter implicitamente tipo "string" em "int" num case

O que significa: o valor do case é de outro tipo que a variável do switch, como case "1": (texto) com int opcao (número).

Como consertar: case 1:, sem aspas. Se a variável é texto, aí sim: case "1":.

error CS1003: Erro de sintaxe, ":" esperado e error CS8515

O que significa: case 1; com ponto e vírgula no lugar dos dois-pontos dá CS1003: Erro de sintaxe, ":" esperado. E switch opcao sem parênteses dá CS8515: É necessário colocar a expressão que rege a switch entre parênteses.

Como consertar: switch (opcao) e case 1:.


Referência rápida

FormaO que faz
switch (x) { … }Escolhe o caso pelo valor de x (número, char, string, bool, enum…).
case 1: … break;Roda quando x vale 1 e sai do switch.
case 1: case 7: … break;Dois rótulos colados: o mesmo bloco para 1 e 7.
case 1 or 7: C# 9O mesmo, com padrão or.
case >= 90: C# 9Caso por comparação (os casos são testados em ordem).
default: … break;Nenhum caso combinou.
goto case X;Continua no caso X, de propósito.
case 1: { … break; }Chaves no caso: variáveis só daquele caso.
Sem breakErro CS0163 (ou CS8070 no último caso).

Exercícios

1. Sair do caso

Complete com a palavra que termina um caso e sai do switch:

case 1: Console.WriteLine("Ver saldo"); ;

2. O caso padrão

Complete com a palavra do caso que roda quando nenhum outro combina:

: Console.WriteLine("Opção inválida"); break;

3. O código do erro

Qual é o código do erro (CS…) que o C# dá quando um caso com instruções fica sem break antes de outro case?

4. O que acontece com case 1: Console.WriteLine("A"); case 2: Console.WriteLine("B"); break; em C#?

5. Com string forma = "Pix";, qual caso roda em switch (forma) { case "pix": … break; default: … break; }?

6. Qual destes é um case válido para switch (dia) com int dia?

Missão final: o caixa da lojinha

Missão: a lojinha da Ana dá 5% de desconto no Pix. Agora ela quer dar o mesmo desconto para pagamento em dinheiro. Faça o "dinheiro" usar o mesmo bloco do "pix", sem copiar as linhas do desconto: cole um case "dinheiro": junto do case "pix": (ou use or). Com R$ 200 em dinheiro, a saída deve ser:

Desconto de 5%: R$ 190,00
string forma = "dinheiro";
decimal total = 200m;
switch (forma)
{
    case "pix":
        total = total * 0.95m;
        Console.WriteLine($"Desconto de 5%: R$ {total:N2}");
        break;
    case "cartão":
        Console.WriteLine($"Sem desconto: R$ {total:N2}");
        break;
    default:
        Console.WriteLine($"Forma desconhecida: {forma}");
        break;
}
string forma = "dinheiro";
decimal total = 200m;
switch (forma)
{
    case "pix":
    case "dinheiro":
        total = total * 0.95m;
        Console.WriteLine($"Desconto de 5%: R$ {total:N2}");
        break;
    case "cartão":
        Console.WriteLine($"Sem desconto: R$ {total:N2}");
        break;
    default:
        Console.WriteLine($"Forma desconhecida: {forma}");
        break;
}

Depois, teste "cartão" (sem desconto: R$ 200,00) e "boleto" (forma desconhecida). Experimente também tirar o break do "cartão" e ler o erro.


Resumo

  • switch (x) escolhe um caso pelo valor de x. Cada case valor: tem as suas instruções, e default: pega o resto.
  • Em C#, nenhum caso cai no seguinte: todo caso com instruções termina com break (ou goto case, return, throw). Sem isso: CS0163, ou CS8070 no último.
  • Rótulos colados (case 1: case 7:) ou case 1 or 7: C# 9 dão o mesmo bloco a vários valores. case >= 90: compara.
  • switch funciona com string, e a comparação é exata (maiúsculas e acentos contam).
  • Os valores dos casos são constantes (CS9135) e não se repetem (CS0152). Para variáveis com o mesmo nome em casos diferentes, use chaves no caso (CS0128).