C# if e else

Até agora, todos os seus programas faziam sempre a mesma coisa, linha por linha, de cima para baixo. Um programa de verdade precisa decidir: se a senha está certa, entra; senão, mostra um erro. Se o estoque está baixo, avisa. Nesta aula você vai aprender o if ("se") e o else ("senão"), as ferramentas de decisão mais usadas do C#, e as armadilhas que pegam todo iniciante: as chaves esquecidas, o = no lugar do == e o ; depois do if.

Você vai aprender
  • Como rodar um bloco de código só quando uma condição é verdadeira, com if.
  • Como escolher entre dois caminhos com if … else.
  • Por que, em C#, a condição tem de ser um bool (e o erro CS0029 quando não é).
  • Por que usar chaves sempre, e onde colocá-las.
  • As armadilhas if (x = 0) e if (x > 0); (aviso CS0642).
  • O que acontece com variáveis criadas dentro do bloco (CS0103) e com variáveis que só ganham valor num dos caminhos (CS0165).

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


A ideia: a bifurcação na estrada

Você está dirigindo e chega numa bifurcação. Uma placa diz: "Se for para a praia, vá pela esquerda; senão, siga pela direita". Você lê a placa, responde à pergunta e segue um dos caminhos, nunca os dois. Logo depois, as duas estradas se juntam de novo e a viagem continua.

Um if é essa placa. Ele faz uma pergunta (a condição), e a resposta, true (sim) ou false (não), decide qual trecho de código roda. As perguntas você já sabe fazer: são as comparações (Operadores de Comparação) e as combinações com &&, || e ! (Operadores Lógicos).


1. O if sozinho

int estoque = 2;
Console.WriteLine($"Estoque: {estoque}");
if (estoque < 5)
{
    Console.WriteLine("Atenção: estoque baixo!");
    Console.WriteLine("Peça mais ao fornecedor.");
}
Console.WriteLine("Fim do relatório.");
$ dotnet run estoque.cs
Estoque: 2
Atenção: estoque baixo!          « 2 < 5 é true: o bloco rodou
Peça mais ao fornecedor.
Fim do relatório.                « esta linha está fora do if: roda sempre
if
Palavra reservada do C#: "se". Sempre em minúsculas (If ou IF não funcionam).
(estoque < 5)
A condição, obrigatoriamente entre parênteses. Ela tem de dar um bool: se der true, o bloco roda; se der false, o bloco é pulado.
{ … }
O bloco: tudo o que está entre as chaves depende do if. Aqui são dois Console.WriteLine, e os dois rodam ou nenhum roda.
sem ; depois do )
A linha do if não termina com ponto e vírgula. O if só termina no fim do bloco. (A seção 5 mostra o que acontece se você puser.)
o recuo
As linhas de dentro ficam 4 espaços mais para a direita. O compilador ignora isso; é para você enxergar o que está dentro do bloco.

Mude o estoque para 10 e o programa mostra só Estoque: 10 e Fim do relatório.: o bloco inteiro é pulado.

Experimente: mude temperatura para valores acima e abaixo de 30 e veja quando o aviso aparece. Depois acrescente um segundo if, que mostra "Leve um casaco." quando a temperatura for menor que 15.

int temperatura = 33;
Console.WriteLine($"Hoje: {temperatura} graus");
if (temperatura > 30)
{
    Console.WriteLine("Beba bastante água!");
}

2. O caminho alternativo: else

Com o else ("senão"), você diz o que fazer quando a condição dá false. Exatamente um dos dois blocos roda, nunca os dois e nunca nenhum.

int idade = 17;
if (idade >= 18)
{
    Console.WriteLine("Pode tirar a carteira de motorista.");
}
else
{
    Console.WriteLine($"Faltam {18 - idade} anos para a carteira.");
}
Console.WriteLine("Fim do programa.");
$ dotnet run idade.cs
Faltam 1 anos para a carteira.          « 17 >= 18 é false: rodou o else
Fim do programa.

Troque o 17 por 30, salve e rode de novo:

$ dotnet run idade.cs
Pode tirar a carteira de motorista.     « 30 >= 18 é true: rodou o if
Fim do programa.
else
Vem logo depois da chave que fecha o bloco do if. Ele não tem condição própria: é "todo o resto".
{18 - idade}
Dentro do bloco você pode fazer contas e usar as variáveis normalmente. Aqui a conta vai direto na interpolação.
Fim do programa.
Depois do if/else, os dois caminhos se juntam e o programa continua normalmente.

"Faltam 1 anos" ficou feio. Para escolher entre "ano" e "anos" numa linha só, existe o operador ternário. Nesta aula a idade está escrita no código; ler do teclado é assunto de Console.ReadLine.

Missão: o programa diz se o número é par, mas para números ímpares ele não mostra nada. Acrescente um else que mostre que o número é ímpar:

7 é ímpar
int n = 7;
if (n % 2 == 0)
{
    Console.WriteLine($"{n} é par");
}
int n = 7;
if (n % 2 == 0)
{
    Console.WriteLine($"{n} é par");
}
else
{
    Console.WriteLine($"{n} é ímpar");
}

O n % 2 é o resto da divisão por 2 (aula Operadores Aritméticos): 0 para pares, 1 para ímpares positivos. Troque o 7 por 10 e por −3 e rode de novo. Com −3, o resto é −1, que também não é 0: cai no else, certinho.


3. A condição tem de ser bool

Em C#, o que vai entre os parênteses do if tem de ser verdadeiro ou falso: uma comparação, uma variável bool (aula char e bool) ou uma combinação delas.

int mensagens = 3;
bool chovendo = false;
if (mensagens > 0)
{
    Console.WriteLine($"Você tem {mensagens} mensagens novas.");
}
if (!chovendo)
{
    Console.WriteLine("Pode deixar o guarda-chuva em casa.");
}
$ dotnet run condicao.cs
Você tem 3 mensagens novas.            « 3 > 0 é true
Pode deixar o guarda-chuva em casa.    « !false é true

Com uma variável bool, escreva if (chovendo) e if (!chovendo), e não if (chovendo == true): ela já é verdadeira ou falsa.

Quem já viu C, JavaScript ou Python pode tentar usar um número direto como condição. Em C#, não dá:

int mensagens = 3;
if (mensagens)
{
    Console.WriteLine("Você tem mensagens.");
}
$ dotnet run numcond.cs
/home/ana/cs/numcond.cs(2,5): error CS0029: Não é possível converter implicitamente tipo "int" em "bool"

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

"Não é possível converter int em bool" (em inglês, Cannot implicitly convert type 'int' to 'bool'). O (2,5) aponta a linha 2, coluna 5: a variável mensagens dentro do if. O C# não adivinha o que você quis perguntar ("é diferente de zero?", "é maior que zero?"); você tem de escrever a pergunta: if (mensagens > 0). Essa regra parece chata, mas é ela que salva você da armadilha da seção 5.


4. Chaves: use sempre

O C# permite tirar as chaves quando o bloco tem uma instrução só: if (x < 0) x = 0;. Parece prático, mas é a origem de um erro traiçoeiro. A Ana quis zerar o saldo quando a compra fosse maior que ele:

decimal saldo = 100m;
decimal compra = 30m;
if (compra > saldo)
    Console.WriteLine("Saldo insuficiente.");
    saldo = 0m;
Console.WriteLine($"Saldo: {saldo}");
$ dotnet run semchaves.cs
Saldo: 0         « a compra cabia no saldo, mas o saldo foi zerado!

Sem chaves, o if controla só a primeira instrução depois dele (o Console.WriteLine). A linha saldo = 0m; está fora do if e roda sempre. O recuo engana os olhos, mas o compilador não olha recuo. E repare: o dotnet compilou sem nenhum aviso. O compilador do C# não reclama do recuo enganoso; quem salva você aqui é o hábito.

Regra do guia: chaves sempre

Use { } em todo if e todo else, mesmo com uma linha só. Quando você (ou outra pessoa) acrescentar uma segunda linha depois, ela já vai cair dentro do bloco. Muitas equipes adotam essa regra, e os analisadores do .NET têm até uma sugestão própria para ela, a IDE0011 ("adicionar chaves"). Um erro desse tipo (uma linha duplicada fora das chaves, o famoso goto fail) deixou uma falha de segurança nos aparelhos da Apple em 2014.

Missão: conserte o programa da Ana com chaves, para o saldo só ser zerado quando a compra for maior que ele. Depois troque a compra para 150m e confira que aí, sim, o saldo zera.

Saldo: 100
decimal saldo = 100m;
decimal compra = 30m;
if (compra > saldo)
    Console.WriteLine("Saldo insuficiente.");
    saldo = 0m;
Console.WriteLine($"Saldo: {saldo}");
decimal saldo = 100m;
decimal compra = 30m;
if (compra > saldo)
{
    Console.WriteLine("Saldo insuficiente.");
    saldo = 0m;
}
Console.WriteLine($"Saldo: {saldo}");

Onde pôr as chaves

O compilador aceita a chave de abertura na linha de baixo ou na mesma linha:

if (nota >= 6)            // estilo do C#: chave na linha de baixo
{
    Console.WriteLine("Aprovada");
}
else
{
    Console.WriteLine("Reprovada");
}

if (nota >= 6) {          // estilo do C, do Java e do JavaScript: chave na mesma linha
    Console.WriteLine("Aprovada");
} else {
    Console.WriteLine("Reprovada");
}

O primeiro estilo (chamado Allman) é o padrão do C#: é o que a Microsoft usa na documentação e no código do próprio .NET, e o que o dotnet format e os editores produzem. Este guia usa ele. O segundo funciona igual e aparece em código de quem veio de outras linguagens. O importante é usar sempre o mesmo no projeto.


5. Duas armadilhas: = e ;

if (x = 0): guardar no lugar de comparar

Um = sozinho guarda um valor; dois, ==, comparam (aula Operadores de Comparação). Em C, if (vidas = 0) compila (no máximo com um aviso) e zera as vidas. Em C#, a regra da seção 3 pega o erro: vidas = 0 dá um int, e o if exige bool.

int vidas = 3;
if (vidas = 0)
{
    Console.WriteLine("Fim de jogo.");
}
$ dotnet run igual.cs
/home/ana/cs/igual.cs(2,5): error CS0029: Não é possível converter implicitamente tipo "int" em "bool"
/home/ana/cs/igual.cs(1,5): warning CS0219: A variável "vidas" é atribuída, mas seu valor nunca é usado

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

O conserto é if (vidas == 0). O aviso CS0219 que veio junto é consequência: como o programa só guarda valores em vidas e nunca lê, o compilador acha a variável inútil. Ele some junto com o erro.

Com uma variável bool o compilador não tem como recusar, porque pronto = true dá mesmo um bool. Então ele avisa:

bool pronto = false;
if (pronto = true)
{
    Console.WriteLine("Pronto!");
}
$ dotnet run igualbool.cs
/home/ana/cs/igualbool.cs(2,5): warning CS0665: Atribuição em expressão condicional é sempre constante. Deseja usar == em vez de = ?
/home/ana/cs/igualbool.cs(1,6): warning CS0219: A variável "pronto" é atribuída, mas seu valor nunca é usado
Pronto!          « pronto era false, mas o if guardou true e entrou

Um aviso não impede o programa de rodar, e o resultado está errado. O próprio aviso sugere o conserto: "Deseja usar == em vez de =?". Melhor ainda: com bool, escreva só if (pronto).

if (…);: o ponto e vírgula que desliga o if

int temperatura = 25;
if (temperatura > 30);
{
    Console.WriteLine("Que calor! Ligue o ventilador.");
}
$ dotnet run pontovirgula.cs
/home/ana/cs/pontovirgula.cs(2,22): warning CS0642: Instrução empty possivelmente incorreta
Que calor! Ligue o ventilador.     « com 25 graus?!
  1. O ; sozinho é uma instrução vazia: "não faça nada".
  2. Então o if ficou assim: "se a temperatura passar de 30, não faça nada".
  3. O bloco { … } da linha de baixo virou um bloco comum, fora do if, e roda sempre.

O aviso, numa tradução meio torta, diz "instrução vazia possivelmente incorreta" (em inglês, Possible mistaken empty statement). A coluna 22 da linha 2 é justamente o ;. Conserto: apague o ;.

Se houver um else, o mesmo ; vira erro de compilação, porque o else fica sem o if dele:

int nota = 4;
if (nota >= 6);
    Console.WriteLine("Aprovada");
else
    Console.WriteLine("Reprovada");
$ dotnet run elsesolto.cs
/home/ana/cs/elsesolto.cs(3,35): error CS8641: 'else' não pode iniciar uma instrução.
/home/ana/cs/elsesolto.cs(3,35): error CS1003: Erro de sintaxe, "(" esperado
/home/ana/cs/elsesolto.cs(3,35): error CS1525: Termo de expressão inválido "else"
/home/ana/cs/elsesolto.cs(3,35): error CS1026: ) esperado
/home/ana/cs/elsesolto.cs(3,35): error CS1002: ; esperado

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

Cinco erros para um ; só! Leia sempre o primeiro: "else não pode iniciar uma instrução". O if terminou no ;, o Console.WriteLine("Aprovada"); é uma instrução solta, e o else chegou sem nenhum if logo antes. Os outros quatro erros são o compilador, já confuso, tentando entender o else como outra coisa. Conserte o primeiro (apague o ; da linha 2, e ponha chaves) e todos somem.

Repare que o aviso CS0642, que apontaria direto o culpado, não aparece aqui: quando encontra erros de sintaxe (de escrita), o compilador para antes de procurar avisos. O editor do guia faz igual e mostra os mesmos cinco erros. Depois que você apaga o ;, não sobra erro nem aviso.


6. Variáveis e o bloco

Criada dentro, só existe dentro

Uma variável declarada dentro das chaves só existe ali dentro. Quando o bloco termina, ela some:

int pontos = 120;
if (pontos > 100)
{
    int bonus = 10;
    pontos = pontos + bonus;
}
Console.WriteLine($"Pontos: {pontos}, bônus: {bonus}");
$ dotnet run escopo.cs
/home/ana/cs/escopo.cs(7,47): error CS0103: O nome "bonus" não existe no contexto atual

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

Na linha 7, fora das chaves, bonus não existe mais. Já pontos, criada fora do if, existe nos dois lugares, e a mudança feita dentro do bloco continua valendo depois. Se você precisa do valor depois do if, declare a variável antes dele. A aula Escopo explica as regras completas.

Valor em um caminho só: CS0165

O C# confere se toda variável tem valor antes de ser lida. Se ela só recebe valor dentro do if, existe um caminho (condição falsa) em que ela fica vazia, e o compilador recusa:

int nota = 8;
string situacao;
if (nota >= 6)
{
    situacao = "Aprovada";
}
Console.WriteLine(situacao);
$ dotnet run naoatribuida.cs
/home/ana/cs/naoatribuida.cs(7,19): error CS0165: Uso de variável local não atribuída "situacao"

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

Mesmo com a nota 8, o compilador não "roda" o programa para ver: ele olha todos os caminhos possíveis. Há dois consertos: dar um valor no else também (else { situacao = "Reprovada"; }), ou dar um valor inicial na declaração (string situacao = "Reprovada";). Com o else, todos os caminhos dão valor, e o erro some.


Formas diferentes de escrever a mesma decisão

FormaExemploQuando usar
if com chavesif (x < 0) { x = 0; }Sempre. É o padrão deste guia.
if sem chavesif (x < 0) x = 0;Compila com uma instrução só, mas evite: é a origem do erro da seção 4.
if + elseif (n % 2 == 0) { … } else { … }Quando há exatamente dois caminhos.
Dois if separadosif (n % 2 == 0) { … }
if (n % 2 != 0) { … }
Evite para caminhos opostos: repete a pergunta e, se você errar uma das duas, os dois blocos podem rodar (ou nenhum). Use else.
Mais de dois caminhosif … else if … elseAula else if.
Escolher um valorvar maior = a > b ? a : b;Aula Operador Ternário.

Quando dá errado

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

O que significa: a condição do if não é verdadeiro/falso. Ou você pôs um número sozinho (if (mensagens)), ou usou = no lugar de == (if (vidas = 0)).

Como consertar: escreva a pergunta completa: if (mensagens > 0), if (vidas == 0).

warning CS0665: Atribuição em expressão condicional é sempre constante

O que significa: você escreveu if (pronto = true), que guarda true em vez de comparar. O programa roda, mas sempre entra no bloco.

Como consertar: if (pronto) (ou if (pronto == true)).

warning CS0642: Instrução empty possivelmente incorreta

O que significa: há um ; logo depois do ) do if. O if não controla nada, e o bloco de baixo roda sempre.

Como consertar: apague o ;: if (temperatura > 30).

error CS8641: 'else' não pode iniciar uma instrução.

O que significa: o else não está logo depois de um if (ou do bloco dele). Causas: um ; depois do if (…), ou um if sem chaves com duas instruções antes do else. Os erros CS1003, CS1525, CS1026 e CS1002 que vêm na mesma linha são consequência dele.

Como consertar: confira se não há ; depois do ) e use chaves: if (…) { … } else { … }.

error CS0103: O nome "bonus" não existe no contexto atual depois de um if

O que significa: a variável foi criada dentro das chaves do if e você a usou fora.

Como consertar: declare a variável antes do if, já com um valor inicial.

error CS0165: Uso de variável local não atribuída "situacao"

O que significa: a variável só recebe valor em um dos caminhos. No outro, ela seria lida sem valor nenhum.

Como consertar: dê um valor também no else, ou dê um valor inicial na declaração.

error CS1003: Erro de sintaxe, "(" esperado
$ cat semparen.cs
int nota = 8;
if nota >= 6
{
    Console.WriteLine("Aprovada");
}
$ dotnet run semparen.cs
/home/ana/cs/semparen.cs(2,4): error CS1003: Erro de sintaxe, "(" esperado
/home/ana/cs/semparen.cs(2,13): error CS1026: ) esperado

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

O que significa: faltaram os parênteses da condição. Em C# eles são obrigatórios (em Python e em Go, não). O compilador mostra onde esperava o ( (linha 2, coluna 4) e o ) (coluna 13, no fim da condição).

Como consertar: if (nota >= 6).


Referência rápida

EstruturaO que faz
if (cond) { A }Roda A só se cond for true.
if (cond) { A } else { B }Roda A se cond for true; senão, roda B. Sempre exatamente um dos dois.
if (ativo) / if (!ativo)Com uma variável bool: "se ativo" / "se não ativo".
if (a > 0 && b > 0)Condição composta (Operadores Lógicos).
if (n) com int nErro CS0029: escreva if (n != 0) ou if (n > 0).
if (x = 0)Erro CS0029 (com int) ou aviso CS0665 (com bool). Use ==.
if (cond);Errado: o ; encerra o if (aviso CS0642). Tire o ;.

Exercícios

1. Complete o if

Complete para mostrar a mensagem só quando saldo for negativo:

(saldo < 0) { Console.WriteLine("Conta no vermelho"); }

2. Senão

Complete com a palavra que introduz o caminho alternativo:

if (nota >= 6) { Console.WriteLine("Aprovada"); } { Console.WriteLine("Reprovada"); }

3. A condição

Complete a condição para o bloco rodar quando vidas for igual a zero:

if (vidas 0) { Console.WriteLine("Fim de jogo"); }

4. O que este trecho mostra, com int x = 5;? if (x > 10) Console.Write("A"); Console.Write("B");

5. Com int pontos = 50;, o que acontece com if (pontos) { … }?

6. Por que if (n > 0); { Console.WriteLine("positivo"); } mostra "positivo" até com n = -3?

Missão final: o frete grátis

Missão: a lojinha da Ana dá frete grátis para compras de 100 reais ou mais; abaixo disso, o frete é 15 reais. Complete com if e else: no caminho do frete grátis, mostre Frete grátis!; no outro, mostre Frete: R$ 15,00 e some 15 ao total. Sem avisos. Com a compra de 120,50, a saída deve ser:

Frete grátis!
Total: R$ 120,50
decimal compra = 120.50m;
decimal total = compra;

// escreva o if e o else aqui

Console.WriteLine($"Total: R$ {total:N2}");
decimal compra = 120.50m;
decimal total = compra;

if (compra >= 100m)
{
    Console.WriteLine("Frete grátis!");
}
else
{
    Console.WriteLine("Frete: R$ 15,00");
    total = total + 15m;
}

Console.WriteLine($"Total: R$ {total:N2}");

Teste também com 99.90m: a saída deve ser Frete: R$ 15,00 e Total: R$ 114,90. E com 100m: frete grátis, porque o "ou mais" pede >=, não >. No código, o número usa ponto (120.50m); na saída, o N2 segue o costume brasileiro e mostra vírgula (aula Formatar Números).


Resumo

  • if (condição) { … } roda o bloco só quando a condição é true. Os parênteses são obrigatórios.
  • else { … } é o caminho alternativo: com if/else, exatamente um dos blocos roda. Depois, o programa continua normalmente.
  • A condição tem de ser bool: um número sozinho dá CS0029. Com variável bool, use if (x) e if (!x).
  • Chaves sempre, no estilo do C# (chave na linha de baixo). Sem elas, o if controla só a primeira instrução, e o compilador não avisa.
  • if (x = 0) dá CS0029 (ou o aviso CS0665 com bool). if (…); desliga o if (aviso CS0642) e, com else, dá CS8641.
  • Variáveis criadas dentro das chaves só existem ali dentro (CS0103). Uma variável lida depois do if precisa de valor em todos os caminhos (CS0165).