C# Operador Ternário (? :)
Na aula de if e else, o programa da Ana mostrou "Faltam 1 anos para a carteira". Para escolher entre "ano" e "anos", daria para escrever um if/else de oito linhas. O operador ternário faz isso numa linha: ele faz uma pergunta e devolve um de dois valores. Chama-se "ternário" porque é o único operador do C# com três partes: a condição, o valor do "sim" e o valor do "não".
- Como escrever
condição ? valorSeSim : valorSeNão. - Por que ele é o mesmo que um
if/elseque dá valor a uma variável. - Como usá-lo dentro de
$"…"(com parênteses, senão:CS8361). - Por que os dois valores precisam ser do mesmo tipo (
CS0173). - Quando não usar: para ações (
CS0201) e em cadeias longas.
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: "se chover, guarda-chuva; senão, boné"
De manhã, a Ana olha pela janela e decide o que levar: "se estiver chovendo, o guarda-chuva; senão, o boné". Ela não faz duas coisas diferentes, ela escolhe uma coisa para levar. A resposta da pergunta é um valor.
O if escolhe qual trecho de código roda. O ternário escolhe qual valor usar. Em C#, a frase da Ana fica assim:
string levar = chovendo ? "guarda-chuva" : "boné";
Leia em voz alta: "levar recebe: está chovendo? então guarda-chuva; senão, boné".
1. O primeiro ternário
int idade = 17;
int faltam = 18 - idade;
string palavra = faltam == 1 ? "ano" : "anos";
Console.WriteLine($"Faltam {faltam} {palavra} para a carteira.");
$ dotnet run ano.cs Faltam 1 ano para a carteira. « faltam == 1 é true: escolheu "ano"
- faltam == 1
- A condição, que tem de dar
bool, como noif. Aqui os parênteses são opcionais. - ?
- "Então…": separa a pergunta das respostas.
- "ano"
- O valor que a expressão inteira vale quando a condição é
true. - :
- "Senão…": separa as duas respostas.
- "anos"
- O valor quando a condição é
false. - string palavra =
- O resultado (um dos dois textos) é guardado na variável, como o resultado de qualquer conta.
Mude a idade para 15 e a saída vira Faltam 3 anos para a carteira.. Só um dos dois valores é calculado: o C# olha a condição primeiro e depois avalia só o lado escolhido.
O mesmo programa com if/else
O ternário não faz nada que um if não faça. É só mais curto. Este programa é equivalente:
int faltam = 3;
string palavra;
if (faltam == 1)
{
palavra = "ano";
}
else
{
palavra = "anos";
}
Console.WriteLine($"Faltam {faltam} {palavra}.");
$ dotnet run ifelse_valor.cs Faltam 3 anos.
Nove linhas contra uma. Quando a decisão é só "qual valor guardar", o ternário deixa o código mais limpo.
Missão: complete a linha de maior com um ternário que escolha o maior entre a e b. Depois troque os valores (por exemplo, a = 50) e confira.
O maior é 30
int a = 12;
int b = 30;
int maior = a; // troque por um ternário
Console.WriteLine($"O maior é {maior}");
int a = 12;
int b = 30;
int maior = a > b ? a : b;
Console.WriteLine($"O maior é {maior}");
(Para o maior de dois números, o .NET também tem Math.Max(a, b), que você vai ver em Matemática.)
2. Dentro de $"…": use parênteses
É muito comum querer o ternário direto na interpolação. Mas veja o que acontece:
int n = 7;
Console.WriteLine($"{n} é {n % 2 == 0 ? "par" : "ímpar"}");
$ dotnet run interp.cs
/home/ana/cs/interp.cs(2,26): error CS8076: Delimitador de fechamento ausente '}' para expressão interpolada iniciada com '{'.
/home/ana/cs/interp.cs(2,28): error CS8361: Uma expressão condicional não pode ser usada diretamente em uma interpolação de cadeia de caracteres porque ‘:’ encerra a interpolação. Use parênteses na expressão condicional.
/home/ana/cs/interp.cs(2,50): error CS1003: Erro de sintaxe, "," esperado
/home/ana/cs/interp.cs(2,55): error CS1003: Erro de sintaxe, "," esperado
Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O editor do guia mostra os mesmos quatro erros. Como são erros de sintaxe, o compilador para neles e não chega a procurar outros.
Lembra do {total:N2} da aula Formatar Números? Dentro das chaves de uma interpolação, o : quer dizer "agora vem o formato". O compilador vê o : do ternário e acha que é o começo de um formato. O erro CS8361 explica isso e já diz o conserto: "Use parênteses na expressão condicional".
int n = 7;
Console.WriteLine($"{n} é {(n % 2 == 0 ? "par" : "ímpar")}");
$ dotnet run interp2.cs 7 é ímpar
Com os parênteses, o : fica "protegido" lá dentro, e o compilador entende que ele é parte do ternário.
3. Os dois lados precisam combinar
O resultado de um ternário tem um tipo só. Por isso, os dois valores precisam ser do mesmo tipo, ou de tipos que o C# saiba converter um no outro. A Ana quis mostrar a quantidade em estoque ou a palavra "esgotado":
int estoque = 0; var mostrar = estoque > 0 ? estoque : "esgotado"; Console.WriteLine(mostrar);
$ dotnet run tipos.cs /home/ana/cs/tipos.cs(2,15): error CS0173: Tipo de expressão condicional não pode ser determinado porque não há conversão implícita entre "int" e "string" Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
Um lado é int (número), o outro é string (texto), e o compilador não sabe qual deve ser o tipo de mostrar. O conserto é deixar os dois lados como texto, com ToString():
int estoque = 0; string mostrar = estoque > 0 ? estoque.ToString() : "esgotado"; Console.WriteLine(mostrar);
$ dotnet run tipos2.cs esgotado
Tipos numéricos "parentes" combinam sem problema: em decimal frete = total >= 100m ? 0m : 15m; os dois lados são decimal, e em double x = ok ? 1 : 2.5; o 1 vira double sozinho (aula Conversão de Tipos).
4. Ternário é para valores, não para ações
Quem acabou de aprender o ternário tenta usá-lo no lugar de qualquer if:
int nota = 8;
nota >= 6 ? Console.WriteLine("Aprovada") : Console.WriteLine("Reprovada");
$ dotnet run acao.cs /home/ana/cs/acao.cs(2,1): error CS0201: Somente as expressões de atribuição, chamada, incremento, diminuição, espera e novo objeto podem ser utilizadas como uma instrução Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O ternário produz um valor, e uma linha que só produz um valor, sem guardar nem usar, não faz nada. Por isso o C# não aceita um ternário sozinho como instrução (em inglês: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement). Além disso, Console.WriteLine não devolve valor nenhum. Há duas formas certas:
// 1. if/else: para fazer coisas diferentes
if (nota >= 6)
{
Console.WriteLine("Aprovada");
}
else
{
Console.WriteLine("Reprovada");
}
// 2. ternário: para escolher o valor, e uma ação só
Console.WriteLine(nota >= 6 ? "Aprovada" : "Reprovada");
A segunda forma funciona porque o ternário agora está dentro de uma chamada: ele escolhe o texto, e o Console.WriteLine mostra.
5. Cuidado com a precedência
O ternário tem precedência muito baixa: quase tudo é calculado antes dele (aula Precedência). Isso pega quem junta texto com +:
bool logado = true; string saudacao = "Olá, " + logado ? "Ana" : "visitante"; Console.WriteLine(saudacao);
$ dotnet run precedencia.cs /home/ana/cs/precedencia.cs(2,19): error CS0029: Não é possível converter implicitamente tipo "string" em "bool" Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O + roda primeiro: "Olá, " + logado vira o texto "Olá, True". Depois, esse texto vira a condição do ternário, e uma condição tem de ser bool. Daí o CS0029, apontando a coluna 19, o começo de "Olá, ". Conserto: parênteses em volta do ternário.
bool logado = true; string saudacao = "Olá, " + (logado ? "Ana" : "visitante"); Console.WriteLine(saudacao);
$ dotnet run precedencia2.cs Olá, Ana
Se o ternário não é a única coisa do lado direito do =, ponha parênteses em volta dele. Nunca atrapalha, e evita os erros desta seção e da seção 2.
6. Ternários encadeados
O "valor do senão" pode ser outro ternário. Assim dá para escolher entre mais de dois valores, como a cadeia de else if:
double nota = 7.5;
string conceito = nota >= 9 ? "A"
: nota >= 7 ? "B"
: nota >= 5 ? "C"
: "D";
Console.WriteLine($"Conceito {conceito}");
$ dotnet run encadeado.cs Conceito B
Funciona, e escrito assim, um por linha, até dá para ler. Mas numa linha só (nota >= 9 ? "A" : nota >= 7 ? "B" : nota >= 5 ? "C" : "D"), vira um quebra-cabeça. Para escolher um valor entre várias faixas, o C# moderno tem uma ferramenta feita para isso, a expressão switch:
string conceito = nota switch
{
>= 9 => "A",
>= 7 => "B",
>= 5 => "C",
_ => "D"
};
Qual usar?
| Situação | Use | Exemplo |
|---|---|---|
| Escolher entre dois valores | Ternário | var palavra = n == 1 ? "item" : "itens"; |
| Fazer coisas diferentes (várias linhas, ações) | if/else | if (erro) { avisar(); parar(); } else { … } |
| Escolher entre três ou mais valores | Expressão switch | nota switch { >= 9 => "A", … } |
Valor padrão quando algo é null | Operador ?? | nome ?? "visitante" (aula Operadores ??, ?. e ??=) |
Quando dá errado
error CS8361: Uma expressão condicional não pode ser usada diretamente em uma interpolação…
O que significa: você pôs um ternário dentro de { } num $"…", e o : dele foi lido como o começo de um formato. Vem acompanhado de CS8076 e CS1003, que são consequência.
Como consertar: parênteses em volta do ternário: {(cond ? "a" : "b")}.
error CS0173: Tipo de expressão condicional não pode ser determinado…
O que significa: os dois valores têm tipos que não combinam, como int e string.
Como consertar: deixe os dois do mesmo tipo: estoque.ToString() no lugar de estoque, ou "0" no lugar de 0.
error CS0201: Somente as expressões de atribuição, chamada… podem ser utilizadas como uma instrução
O que significa: um ternário sozinho na linha, sem guardar o resultado em lugar nenhum, normalmente com uma ação em cada lado.
Como consertar: use if/else, ou ponha o ternário dentro da ação: Console.WriteLine(ok ? "sim" : "não");.
error CS0029: Não é possível converter implicitamente tipo "string" em "bool" num ternário
O que significa: algo antes do ? (normalmente um +) foi calculado primeiro e virou a condição.
Como consertar: parênteses em volta do ternário: "Olá, " + (logado ? "Ana" : "visitante").
error CS1003: Erro de sintaxe, ":" esperado
$ cat semtres.cs int n = 7; string tipo = n % 2 == 0 ? "par"; Console.WriteLine(tipo); $ dotnet run semtres.cs /home/ana/cs/semtres.cs(2,33): error CS1003: Erro de sintaxe, ":" esperado /home/ana/cs/semtres.cs(2,33): error CS1525: Termo de expressão inválido ";" Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O que significa: o ternário está sem a terceira parte. Diferente do if, que pode ficar sem else, o ternário sempre precisa do valor do "senão": a variável tem de receber alguma coisa nos dois casos.
Como consertar: n % 2 == 0 ? "par" : "ímpar".
Referência rápida
| Forma | O que faz |
|---|---|
cond ? a : b | Vale a se cond for true; senão, vale b. Só o lado escolhido é calculado. |
var x = cond ? a : b; | O mesmo que um if/else que dá valor a x. |
$"{(cond ? a : b)}" | Dentro da interpolação, sempre com parênteses. |
"texto" + (cond ? a : b) | Com + ou outra conta, parênteses em volta. |
c1 ? a : c2 ? b : c | Encadeado: funciona, mas prefira a expressão switch. |
Tipos de a e b | Precisam combinar (CS0173 se não). |
Exercícios
1. Complete o ternário
Complete os dois símbolos que faltam:
2. Qual o valor?
Com int x = 4;, quanto vale x > 5 ? 10 : 20?
3. Na interpolação
Complete o que falta para esta linha compilar (dois caracteres, um de cada lado):
4. Por que var x = ok ? 1 : "um"; não compila?
5. Qual destas linhas é um bom uso do ternário?
Missão final: o carrinho da lojinha
Missão: o carrinho da Ana tem 1 item e R$ 120 de compras. O programa está com dois erros: mostra "1 itens" e cobra frete de quem passou de R$ 100. Use dois ternários:
- um que escolha
"item"ou"itens"(dentro da interpolação, não esqueça os parênteses); - um que dê ao
freteo valor0mquandototal >= 100m, e15msenão.
A linha do textoFrete já está pronta (e é um ternário também). A saída deve ser:
Você tem 1 item no carrinho. Frete: grátis
int itens = 1;
decimal total = 120m;
Console.WriteLine($"Você tem {itens} itens no carrinho.");
decimal frete = 15m;
string textoFrete = frete == 0m ? "grátis" : $"R$ {frete:N2}";
Console.WriteLine($"Frete: {textoFrete}");
int itens = 1;
decimal total = 120m;
Console.WriteLine($"Você tem {itens} {(itens == 1 ? "item" : "itens")} no carrinho.");
decimal frete = total >= 100m ? 0m : 15m;
string textoFrete = frete == 0m ? "grátis" : $"R$ {frete:N2}";
Console.WriteLine($"Frete: {textoFrete}");
Depois, troque para itens = 3 e total = 59.90m: a saída deve virar Você tem 3 itens no carrinho. e Frete: R$ 15,00.
Resumo
condição ? valorSeSim : valorSeNãoescolhe um de dois valores. É o único operador do C# com três partes.- É o mesmo que um
if/elseque dá valor a uma variável, só que numa linha. Só o lado escolhido é calculado. - Dentro de
$"…", ponha parênteses (CS8361). Junto com+, também (CS0029). - Os dois valores precisam ter tipos que combinem (
CS0173), e a terceira parte é obrigatória. - Para ações, use
if/else(CS0201). Para três ou mais valores, prefira a expressão switch.