If mais limpo em C#: ternário, retorno antecipado e pattern matching

Mascote do Devs na Prática com lupa ao lado do título If mais limpo em C#

Resumo rápido

  • Este post é para quem já sabe usar if, else e else if e quer escrever condições mais fáceis de ler.
  • Ternário (? :) escolhe entre dois valores numa linha.
  • Retorno antecipado acaba com a escada de if dentro de if.
  • Condições longas ficam melhores com um nome: uma variável bool ou um método.
  • is e a expressão switch com padrões testam faixas e tipos sem repetir a variável.
  • ?? dá um valor padrão quando algo é null.

Se você ainda está aprendendo o básico, comece pelo post If, else e else if em C#. Aqui o assunto é outro: o seu if já funciona, mas está difícil de ler. Vamos ver cinco técnicas para deixar as decisões do jogo mais limpas.

O problema: a escada de if

Mascote tapando os olhos diante de if aninhados

Abrir a porta do laboratório exige estar vivo, ter o cartão e ter energia no prédio. Escrito de qualquer jeito, vira isto:

C#

Console.WriteLine(TentarAbrir(80, true, false));

static string TentarAbrir(int vida, bool temCartao, bool temEnergia)
{
    if (vida > 0)
    {
        if (temCartao)
        {
            if (temEnergia)
            {
                return "Porta aberta.";
            }
            else
            {
                return "Sem energia no prédio.";
            }
        }
        else
        {
            return "Você precisa do cartão.";
        }
    }
    else
    {
        return "Você está morto.";
    }
}

Saída esperada

Sem energia no prédio.

Funciona, mas para achar qual mensagem pertence a qual if você precisa contar chaves. Cada nova regra empurra o código mais para a direita.

1. Retorno antecipado

Mascote com uma ideia ao lado do código com retorno antecipado

Inverta as condições e saia do método assim que algo der errado. O caminho feliz fica no fim, sem nenhuma chave em volta:

C#

Console.WriteLine(TentarAbrir(80, true, false));
Console.WriteLine(TentarAbrir(80, true, true));

static string TentarAbrir(int vida, bool temCartao, bool temEnergia)
{
    if (vida <= 0) return "Você está morto.";
    if (!temCartao) return "Você precisa do cartão.";
    if (!temEnergia) return "Sem energia no prédio.";

    return "Porta aberta.";
}

Saída esperada

Sem energia no prédio.
Porta aberta.

Mesmo comportamento, metade das linhas e nenhuma escada. Cada regra é uma linha que se lê sozinha. Essas primeiras verificações têm até nome: guard clauses (cláusulas de guarda).

2. Ternário para escolher um valor

Quando o if só serve para escolher entre dois valores, o operador ternário faz isso numa linha: condição ? valorSeVerdadeiro : valorSeFalso.

C#

int vida = 35;

string estado = vida < 50 ? "ferido" : "bem";
int velocidade = vida < 50 ? 3 : 5;

Console.WriteLine($"Você está {estado} e anda a {velocidade} m/s.");

Saída esperada

Você está ferido e anda a 3 m/s.

Use o ternário para valores, nunca para ações. E evite ternário dentro de ternário: com três ou mais opções, um switch fica mais claro.

3. Dê nome às condições

Uma condição grande no if obriga quem lê a decifrar a regra. Guarde o resultado numa variável bool com nome:

C#

int vida = 60, balas = 0, inimigosPerto = 3;
bool temFaca = true, ehNoite = true;

bool podeLutar = vida > 30 && (balas > 0 || temFaca);
bool situacaoPerigosa = inimigosPerto > 2 && ehNoite;

if (podeLutar && !situacaoPerigosa)
    Console.WriteLine("Lute!");
else
    Console.WriteLine("Melhor se esconder.");

Saída esperada

Melhor se esconder.

Compare com if (vida > 30 && (balas > 0 || temFaca) && !(inimigosPerto > 2 && ehNoite)). A regra é a mesma, mas os nomes explicam o porquê. Se a condição for usada em vários lugares, transforme-a num método, como PodeLutar().

4. Pattern matching: faixas sem repetir a variável

Mascote confiante ao lado de uma expressão switch com padrões

O C# moderno deixa testar faixas de valores com is e com a expressão switch, sem escrever o nome da variável a cada comparação:

C#

foreach (int vida in new[] { 100, 65, 20, 0 })
{
    string estado = vida switch
    {
        0 => "morto",
        < 30 => "crítico",
        < 70 => "ferido",
        _ => "saudável"
    };
    Console.WriteLine($"{vida,3}: {estado}");
}

int municao = 7;
if (municao is > 0 and < 10)
    Console.WriteLine("Pouca munição, economize.");

Saída esperada

100: saudável
 65: ferido
 20: crítico
  0: morto
Pouca munição, economize.

No switch, cada linha é testada em ordem e o _ é o "qualquer outro valor". Com is você pode juntar padrões com and, or e not: municao is > 0 and < 10 lê quase como português. O {vida,3} só alinha o número em 3 casas.

5. ?? para valores que podem faltar

Quando um texto pode ser null, o if de "se estiver vazio, use o padrão" vira um operador só, o ??:

C#

string? nomeDigitado = null;

string nome = nomeDigitado ?? "Sobrevivente";
Console.WriteLine($"Bem-vindo, {nome}.");

Saída esperada

Bem-vindo, Sobrevivente.

Leia a ?? b como "use a; se for nulo, use b". Existe também o ??=: nome ??= "Sobrevivente"; só preenche se estiver nulo.

Qual técnica usar

SituaçãoTécnica
if dentro de if dentro de ifretorno antecipado
if só para escolher entre dois valoresternário ? :
condição longa e difícil de lervariável bool ou método com nome
várias faixas de um mesmo valorexpressão switch com padrões
"se for nulo, use este padrão"?? ou ??=

Erros comuns

1. Comparar bool com true. if (temCartao == true) funciona, mas if (temCartao) diz o mesmo com menos ruído. Para o contrário, use !temCartao.

2. if que só devolve true ou false. if (vida > 0) return true; else return false; é só return vida > 0;.

3. Ordem errada no switch. Se < 70 vier antes de < 30, a vida 20 cai em "ferido", porque a primeira regra que combina vence. Vá do caso mais específico para o mais geral.

DESAFIO

Tente resolver antes de abrir a resposta. Sobreviveu? Vá para o próximo.

  1. Reescreva com ternário: string arma; if (balas > 0) arma = "pistola"; else arma = "faca";

    Ver resposta

    string arma = balas > 0 ? "pistola" : "faca";

  2. Simplifique: static bool EstaVivo(int vida) { if (vida > 0) { return true; } else { return false; } }

    Ver resposta

    static bool EstaVivo(int vida) => vida > 0;

  3. Escreva uma expressão switch que transforma o número de zumbis em "livre" (0), "cuidado" (1 a 3) e "fuja" (4 ou mais).

    Ver resposta

    string alerta = zumbis switch { 0 => "livre", <= 3 => "cuidado", _ => "fuja" };

Conclusão

Código que só funciona é o mínimo. Código que se lê de primeira é o que você (e o seu eu do futuro) agradece. Retorno antecipado, nomes nas condições e pattern matching resolvem a maior parte dos if confusos.

📖 Revise a base: If, else e else if em C# e Switch em C#.

Comentários

Postagens mais visitadas deste blog

Classes e objetos em C#: criando o seu personagem do jeito certo

Console.WriteLine e Console.ReadLine em C#: conversando com o jogador

Modificadores de acesso em C#: public, private, protected e internal