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.
- As partes do
switch:case,breakedefault. - Por que o C# não deixa um
casecair no seguinte (errosCS0163eCS8070). - Como juntar vários valores no mesmo bloco, e o
case 1 or 7:C# 9. switchcom texto (string), e por que"PIX"não é"pix".- Variáveis dentro de um
case(CS0128),caserepetido (CS0152) ecasecom 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
switche continua depois da chave de fechamento. Em C#, todo caso precisa terminar com umbreak(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:
| Comando | O que faz | Onde 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ção | Use |
|---|---|
| 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ável | Os dois servem; o switch com case >= 90: fica mais limpo quando são muitas |
| Escolher um valor (não uma ação) para cada caso | Expressã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
| Forma | O 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# 9 | O mesmo, com padrão or. |
case >= 90: C# 9 | Caso 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 break | Erro CS0163 (ou CS8070 no último caso). |
Exercícios
1. Sair do caso
Complete com a palavra que termina um caso e sai do switch:
2. O caso padrão
Complete com a palavra do caso que roda quando nenhum outro combina:
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 dex. Cadacase valor:tem as suas instruções, edefault:pega o resto.- Em C#, nenhum caso cai no seguinte: todo caso com instruções termina com
break(ougoto case,return,throw). Sem isso:CS0163, ouCS8070no último. - Rótulos colados (
case 1: case 7:) oucase 1 or 7:C# 9 dão o mesmo bloco a vários valores.case >= 90:compara. switchfunciona comstring, 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).