Decisões com condicionais
por Sloane KensingtonEntenda o conceito
Até aqui os programas executavam todas as linhas, de cima para baixo. Com condicionais, o programa passa a escolher: if (condição) { ... } só executa o bloco quando a condição é verdadeira, e o else define o que fazer no caso contrário. Em C#, a condição precisa ser obrigatoriamente um bool: if (quantidade) não compila, é preciso escrever a comparação, como if (quantidade > 0).
As condições são montadas com os operadores de comparação (==, !=, <, <=, >, >=) e combinadas com os operadores lógicos && (e), || (ou) e ! (não). Quando há mais de duas saídas, encadeie else if: as condições são testadas em ordem e só o bloco da primeira verdadeira é executado.
Para escolher entre dois valores numa só expressão existe o operador ternário (condição ? valorSeVerdadeiro : valorSeFalso). E, quando a decisão depende de um único valor, o switch deixa o código mais limpo: o switch tradicional, com case e break, e a switch expression (C# 8+), que devolve um valor e aceita padrões como < 10 e _ para "qualquer outro".
- A condição do
iftem de ser umbool: C# não converte números em verdadeiro ou falso, entãoif (estoque)é erro de compilação. - Numa cadeia de
else if, só o bloco da primeira condição verdadeira roda; por isso, teste os casos mais específicos primeiro. &&e||fazem curto-circuito: se o lado esquerdo já decide o resultado, o direito nem é avaliado.- Compare com
==, não com=:if (cupom = "PRIMEIRA")tenta atribuir e não compila, porque o resultado não ébool. - No
switchtradicional cadacasetermina embreak; a switch expression (valor switch { ... }) devolve um valor e usa_como caso padrão.
Do conceito à prática
3 exemplos para ver a ideia funcionando.
01Devolução de livro com if, else e ternário
// Devolução de livro na biblioteca: if e else escolhem um caminho.
const int PrazoDias = 14;
const int MultaPorDiaCentavos = 150;
int diasEmprestado = 17;
bool leitorEstudante = true;
Console.WriteLine($"Livro devolvido após {diasEmprestado} dias (prazo: {PrazoDias}).");
// A condição entre parênteses precisa ser um bool
if (diasEmprestado <= PrazoDias)
{
Console.WriteLine("Devolução no prazo. Obrigado!");
}
else
{
int diasAtraso = diasEmprestado - PrazoDias;
int multa = diasAtraso * MultaPorDiaCentavos;
// if sem else: o bloco só roda quando a condição é verdadeira
if (leitorEstudante)
{
multa = multa / 2;
Console.WriteLine("Desconto de estudante aplicado: 50%.");
}
Console.WriteLine($"Atraso de {diasAtraso} dias. Multa: R$ {multa / 100},{multa % 100:D2}");
}
// O operador ternário escolhe entre dois valores numa só expressão
string situacao = diasEmprestado > PrazoDias ? "pendente de pagamento" : "liberado";
Console.WriteLine($"Situação do leitor: {situacao}");
O if principal divide o programa em dois caminhos: devolução no prazo ou com atraso. Dentro do else aparece um segundo if, sem else, que só aplica o desconto quando o leitor é estudante; se a condição fosse falsa, o programa simplesmente pularia o bloco. Como 17 > 14, o caminho do atraso é o escolhido e a multa de 3 dias, já com o desconto, fica em R$ 2,25.
No fim, o operador ternário escolhe um entre dois textos numa única linha. Ele é ideal para atribuições simples como essa; quando cada caminho precisa de várias instruções, o if/else com blocos continua sendo mais legível.
02Conceito do boletim com else if e operadores lógicos
// Boletim: else if testa as faixas em ordem; && e || combinam condições.
int nota = 68; // de 0 a 100
int frequencia = 82; // em porcentagem
bool fezRecuperacao = false;
// A primeira condição verdadeira vence; as demais nem são testadas
string conceito;
if (nota >= 90)
{
conceito = "A";
}
else if (nota >= 75)
{
conceito = "B";
}
else if (nota >= 60)
{
conceito = "C";
}
else
{
conceito = "D";
}
bool frequenciaOk = frequencia >= 75;
Console.WriteLine($"Nota: {nota} | Frequência: {frequencia}%");
Console.WriteLine($"Conceito: {conceito}");
// && exige as duas condições; ! inverte um bool
if (conceito != "D" && frequenciaOk)
{
Console.WriteLine("Resultado: aprovado");
}
else if (frequenciaOk && !fezRecuperacao)
{
Console.WriteLine("Resultado: em recuperação");
}
else
{
Console.WriteLine("Resultado: reprovado");
}
// || basta uma das condições ser verdadeira
bool quadroDeHonra = nota >= 90 || (nota >= 85 && frequencia == 100);
Console.WriteLine($"Quadro de honra: {(quadroDeHonra ? "sim" : "não")}");
A cadeia de else if testa as faixas da maior para a menor. Com nota 68, as duas primeiras condições são falsas e nota >= 60 é a primeira verdadeira, então o conceito é C e o else final nem é avaliado. A ordem importa: se nota >= 60 viesse primeiro, uma nota 95 também cairia nela.
A segunda decisão combina condições: && exige conceito diferente de D e frequência suficiente, e !fezRecuperacao inverte o bool. Na última linha, || aceita qualquer uma das duas formas de entrar no quadro de honra; os parênteses deixam claro qual parte é avaliada junta.
03Turno de jogo com switch e switch expression
// Turno de um jogo de aventura: switch para comandos e para o dado.
string comando = "leste";
int dado = 17; // resultado de um d20 já sorteado
// switch statement: compara o valor com cada case
switch (comando)
{
case "norte":
case "sul":
Console.WriteLine("Você segue pela estrada principal.");
break;
case "leste":
Console.WriteLine("Você entra na floresta sombria.");
break;
case "oeste":
Console.WriteLine("Você volta para a vila.");
break;
default:
Console.WriteLine("Comando desconhecido.");
break;
}
// switch expression com padrões relacionais (C# 9+):
// os braços são testados em ordem e o primeiro que casar vence
string resultado = dado switch
{
1 => "falha crítica",
< 10 and > 1 => "errou o golpe",
< 20 and >= 10 => "acertou o golpe",
20 => "acerto crítico",
_ => "valor de dado inválido"
};
Console.WriteLine($"Dado: {dado} -> {resultado}");
// O braço _ (descarte) cobre qualquer valor que sobrou
int bonusOuro = resultado switch
{
"acerto crítico" => 50,
"acertou o golpe" => 10,
_ => 0
};
Console.WriteLine($"Ouro ganho no turno: {bonusOuro}");
O switch tradicional compara comando com cada case. Dois rótulos seguidos (case "norte": e case "sul":) compartilham o mesmo bloco, e cada bloco termina com break, obrigatório em C# para não "cair" no próximo caso. O default trata qualquer texto que não foi previsto.
A switch expression devolve um valor diretamente para a variável. Os padrões relacionais (< 10, >= 10) combinados com and definem faixas do dado, e o _ cobre o que sobrou, como um dado 0 ou 25. O segundo switch mostra que a técnica também funciona com textos, transformando o resultado em ouro ganho.
Agora é com você
Tente resolver no rascunho antes de abrir a resposta. Os códigos não são executados nesta página.
01Alerta de estoque da lojaFácil
Escreva um programa que mostre o nome e a quantidade em estoque de um produto e classifique a situação: esgotado quando a quantidade for zero, repor estoque quando estiver abaixo do mínimo de 5 unidades e ok nos demais casos.
- Guardar o estoque mínimo numa
const intcom valor 5. - Usar uma única cadeia
if/else if/elsepara as três situações. - Testar a quantidade 3 e produzir a saída idêntica à esperada.
const int EstoqueMinimo = 5;
string produto = "Caneca de cerâmica";
int quantidade = 3;
Console.WriteLine($"Produto: {produto}");
Console.WriteLine($"Estoque: {quantidade} unidades");
// classifique a situação com if / else if / else
Produto: Caneca de cerâmica
Estoque: 3 unidades
Situação: repor estoque
Ver dica
Pense na ordem dos testes: zero também é menor que 5, então o caso esgotado precisa ser verificado antes do caso repor estoque.
Ver resposta
// Alerta de estoque: três situações possíveis, testadas em ordem
const int EstoqueMinimo = 5;
string produto = "Caneca de cerâmica";
int quantidade = 3;
Console.WriteLine($"Produto: {produto}");
Console.WriteLine($"Estoque: {quantidade} unidades");
if (quantidade == 0)
{
Console.WriteLine("Situação: esgotado");
}
else if (quantidade < EstoqueMinimo)
{
Console.WriteLine("Situação: repor estoque");
}
else
{
Console.WriteLine("Situação: ok");
}
Passo a passo
const int EstoqueMinimo = 5;dá nome ao limite; se a loja mudar a regra, basta alterar uma linha.- As duas primeiras chamadas a
Console.WriteLinemostram o produto e a quantidade com interpolação. if (quantidade == 0)trata o caso mais específico primeiro: produto esgotado.else if (quantidade < EstoqueMinimo)só é testado se a quantidade não for zero; com 3 unidades, essa condição é verdadeira.- O
elsefinal cobre todo o resto, ou seja, 5 unidades ou mais. - Como só um bloco da cadeia é executado, o programa imprime exatamente uma linha de situação.
Por que funciona
Numa cadeia de else if, as condições são avaliadas de cima para baixo e a primeira verdadeira encerra a decisão. Colocando o zero antes do limite mínimo, cada quantidade cai em uma única faixa, sem sobreposição.
Erros comuns
- Inverter a ordem: testar
quantidade < EstoqueMinimoprimeiro faz o zero cair emrepor estoque, e oesgotadonunca aparece. Teste o caso específico antes. - Usar
=na comparação:if (quantidade = 0)gera o erro CS0029, pois a atribuição resulta emint, não embool. Use==. - Três
ifindependentes: sem oelse, uma quantidade 0 imprimiriaesgotadoe tambémrepor estoque. Oelse ifgarante que só uma linha saia.
Outra forma de resolver
Com uma switch expression e padrões relacionais: string situacao = quantidade switch { 0 => "esgotado", < EstoqueMinimo => "repor estoque", _ => "ok" };. Fica mais compacta e é preferível quando cada caso só escolhe um valor; o if encadeado é melhor quando cada caminho executa várias instruções.
Saída esperada
Produto: Caneca de cerâmica
Estoque: 3 unidades
Situação: repor estoque
02Cupom e frete no fechamento do carrinhoMédio
Calcule o fechamento de um carrinho de R$ 140: o cupom PRIMEIRA dá 10% de desconto e o frete de R$ 20 é grátis para clientes VIP ou para compras de R$ 150 ou mais, já com o desconto. Mostre o subtotal, o desconto, o frete (o texto grátis ou o valor) e o total.
- Aplicar o desconto com um
ifque compara o cupom com==. - Decidir o frete com uma única condição usando
||e o operador ternário. - Guardar o valor mínimo para frete grátis e o valor do frete em constantes.
- Usar
clienteVip = falsee produzir a saída idêntica à esperada.
const int FreteGratisAPartirDe = 150;
const int ValorFrete = 20;
int subtotal = 140;
string cupom = "PRIMEIRA";
bool clienteVip = false;
// calcule o desconto do cupom
// decida o frete com || e o operador ternário
// imprima subtotal, desconto, frete e total
Subtotal: R$ 140
Desconto do cupom: R$ 14
Frete: R$ 20
Total a pagar: R$ 146
Ver dica
O frete grátis depende do valor já com desconto: calcule o total dos produtos antes de montar a condição do frete.
Ver resposta
// Fechamento do carrinho: cupom e frete decididos por condições
const int FreteGratisAPartirDe = 150;
const int ValorFrete = 20;
int subtotal = 140; // em reais
string cupom = "PRIMEIRA";
bool clienteVip = false;
// Cupom válido dá 10% de desconto; qualquer outro texto não dá nada
int desconto = 0;
if (cupom == "PRIMEIRA")
{
desconto = subtotal * 10 / 100;
}
int totalProdutos = subtotal - desconto;
// || : basta ser VIP ou atingir o valor mínimo para o frete ser grátis
int frete = clienteVip || totalProdutos >= FreteGratisAPartirDe ? 0 : ValorFrete;
Console.WriteLine($"Subtotal: R$ {subtotal}");
Console.WriteLine($"Desconto do cupom: R$ {desconto}");
Console.WriteLine($"Frete: {(frete == 0 ? "grátis" : $"R$ {frete}")}");
Console.WriteLine($"Total a pagar: R$ {totalProdutos + frete}");
Passo a passo
- As constantes guardam as regras da loja (mínimo para frete grátis e valor do frete) com nomes claros.
descontocomeça em0e oif (cupom == "PRIMEIRA")só o altera quando o cupom é válido; assim a variável sempre tem valor, com ou sem cupom.subtotal * 10 / 100multiplica antes de dividir, o que dá14sem perder nada na divisão inteira.totalProdutosguarda o valor já com desconto,126, que é o usado na regra do frete.clienteVip || totalProdutos >= FreteGratisAPartirDeé falso nos dois lados, então o ternário escolheValorFrete, ou seja,20.- Na impressão, outro ternário troca o
0pelo textográtis; como o frete é 20, apareceR$ 20.
Por que funciona
Cada regra do enunciado virou uma condição: o cupom é uma igualdade de texto e o frete grátis é um "ou" entre duas situações. Como || é verdadeiro quando qualquer lado é verdadeiro, ser VIP já bastaria; aqui nenhum lado vale e o frete é cobrado.
Erros comuns
- Usar o subtotal na regra do frete:
subtotal >= FreteGratisAPartirDecom R$ 140 dá o mesmo resultado aqui, mas falha com um carrinho de R$ 160 e cupom, que pagaria frete. Use o valor com desconto. - Trocar
||por&&:clienteVip && totalProdutos >= 150exigiria as duas coisas, e um cliente comum com compra alta pagaria frete. - Comparar a caixa do texto sem cuidado:
cupom == "primeira"é falso, porque==entre strings diferencia maiúsculas de minúsculas. Use exatamente"PRIMEIRA".
Outra forma de resolver
O desconto também cabe num ternário: int desconto = cupom == "PRIMEIRA" ? subtotal * 10 / 100 : 0;. É preferível quando há um único cupom; com vários cupons, um switch sobre o texto do cupom fica mais organizado.
Saída esperada
Subtotal: R$ 140
Desconto do cupom: R$ 14
Frete: R$ 20
Total a pagar: R$ 146
03Ataques contra o chefe da masmorraDesafio
Escreva uma função local Atacar(string arma, int dado) que use uma switch expression para o dano base da arma e uma cadeia if / else if para o efeito do dado, imprima uma linha por ataque e devolva o dano. Chame-a para quatro ataques, some os danos e diga se o chefe, com 30 de vida, foi derrotado.
- Dano base pela switch expression:
espada8,machado10,arco6 e qualquer outra arma 2, usando_. - Efeito do dado em ordem: 1 erra (dano 0), 20 é crítico (dobro), 15 ou mais dá bônus (+2) e o resto é normal.
- Atacar com machado (20), arco (1), espada (16) e cajado (9), acumulando o dano total com
+=. - Decidir a última linha com o operador ternário comparando o total com uma
const intde valor 30.
const int VidaChefe = 30;
int total = 0;
// chame Atacar para os quatro ataques e acumule o dano
// imprima o dano total e se o chefe foi derrotado
static int Atacar(string arma, int dado)
{
// dano base com switch expression
// efeito do dado com if / else if
return 0;
}
machado (dado 20): crítico, dano 20
arco (dado 1): errou, dano 0
espada (dado 16): bônus, dano 10
cajado (dado 9): normal, dano 2
Dano total: 32
O chefe foi derrotado!
Ver dica
O dado 20 também é maior ou igual a 15: teste o 20 antes do bônus, senão o crítico nunca acontece.
Ver resposta
// Ataques contra o chefe da masmorra: switch expression e if/else if
const int VidaChefe = 30;
int total = 0;
total += Atacar("machado", 20);
total += Atacar("arco", 1);
total += Atacar("espada", 16);
total += Atacar("cajado", 9);
Console.WriteLine($"Dano total: {total}");
Console.WriteLine(total >= VidaChefe ? "O chefe foi derrotado!" : "O chefe ainda está de pé.");
// Calcula, imprime e devolve o dano de um ataque
static int Atacar(string arma, int dado)
{
// switch expression: cada arma tem um dano base
int danoBase = arma switch
{
"espada" => 8,
"machado" => 10,
"arco" => 6,
_ => 2 // arma desconhecida: dano mínimo
};
// Casos especiais primeiro: 1 e 20 vencem o teste do bônus
int dano;
string efeito;
if (dado == 1)
{
dano = 0;
efeito = "errou";
}
else if (dado == 20)
{
dano = danoBase * 2;
efeito = "crítico";
}
else if (dado >= 15)
{
dano = danoBase + 2;
efeito = "bônus";
}
else
{
dano = danoBase;
efeito = "normal";
}
Console.WriteLine($"{arma} (dado {dado}): {efeito}, dano {dano}");
return dano;
}
Passo a passo
const int VidaChefe = 30;guarda a vida do chefe; a decisão final compara o total com esse nome, não com um número solto.- Cada
total += Atacar(...)chama a função, que imprime a linha do ataque e devolve o dano, somado ao acumulador. - Dentro de
Atacar, a switch expression transforma o nome da arma em dano base;_ => 2cobre ocajadoe qualquer arma não prevista. - A cadeia
if/else iftesta primeiro os casos especiais:dado == 1zera o dano edado == 20dobra o dano base. - Só depois vem
dado >= 15, que soma 2 de bônus; por isso o 16 da espada dá8 + 2 = 10. - O
elsefinal mantém o dano base, como no ataque com dado 9. danoeefeitosão declarados sem valor e recebem um em todos os caminhos; o compilador confere isso e aceita a leitura depois da cadeia.- Na linha final, o ternário compara
32 >= 30e escolhe a mensagem de vitória.
Por que funciona
As duas decisões têm formatos diferentes, e cada uma usa a ferramenta adequada: a arma é um único valor mapeado para outro, trabalho típico da switch expression; o dado tem regras com prioridade, que a ordem do else if resolve, porque só a primeira condição verdadeira é executada. Somando os retornos (20 + 0 + 10 + 2), o total passa da vida do chefe.
Erros comuns
- Testar o bônus antes do crítico: com
else if (dado >= 15)antes dedado == 20, o machado faria 12 em vez de 20, e o total cairia para 24, sem derrotar o chefe. - Esquecer o
_na switch expression: o compilador avisa (CS8509) que nem todos os valores são tratados, e ocajadolançariaSwitchExpressionExceptionao rodar. - Deixar um caminho sem atribuição: se o
elsefinal for removido, lerdanogera o erro CS0165 (variável local não atribuída). Garanta um valor em todo caminho. - Esquecer o
return dano;no fim da função: como ela foi declarada com retornoint, o compilador acusa o erro CS0161 (nem todos os caminhos de código retornam um valor).
Outra forma de resolver
O efeito do dado também pode virar uma switch expression com tupla: (dano, efeito) = dado switch { 1 => (0, "errou"), 20 => (danoBase * 2, "crítico"), >= 15 => (danoBase + 2, "bônus"), _ => (danoBase, "normal") };. Fica mais curta e o compilador ainda confere a cobertura dos casos; a cadeia de if é preferível quando cada caso precisa de várias instruções ou de mensagens extras.
Saída esperada
machado (dado 20): crítico, dano 20
arco (dado 1): errou, dano 0
espada (dado 16): bônus, dano 10
cajado (dado 9): normal, dano 2
Dano total: 32
O chefe foi derrotado!