C# Precedência
Quanto é 2 + 3 * 4? Se você fizer da esquerda para a direita, dá 20. Se fizer a multiplicação primeiro, dá 14. O C# precisa de uma regra para decidir, e essa regra se chama precedência: a ordem em que os operadores são calculados quando aparecem juntos. Nesta aula você vai ver essa ordem, o que acontece quando dois operadores têm a mesma prioridade, os erros clássicos (a média sem parênteses e o texto que "gruda" nos números) e como os parênteses resolvem tudo.
- O que é precedência e por que
2 + 3 * 4dá 14. - A associatividade: por que
10 - 4 - 3dá 3 ea = b = 7funciona. - Os erros clássicos: a média sem parênteses e
"Total: " + 2 + 3mostrandoTotal: 23. - Onde o
(double), o!, as comparações e o&&/||entram na fila. - A tabela completa dos operadores do C# e a regra de ouro: na dúvida, parênteses.
Todos os programas desta aula foram rodados com o .NET 10 na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/cs, com dotnet run arquivo.cs.
A ideia: a fila do caixa preferencial
Numa fila de supermercado com atendimento preferencial, quem tem prioridade passa na frente, mesmo tendo chegado depois. Entre duas pessoas com a mesma prioridade, vale a ordem de chegada.
Numa conta, os operadores são as pessoas da fila. A precedência diz quem tem prioridade (a multiplicação passa na frente da soma). A associatividade diz o que fazer quando dois têm a mesma prioridade (quase sempre, quem está mais à esquerda vai primeiro). E os parênteses são o gerente: o que está dentro deles é atendido antes de tudo.
1. Multiplicação antes da soma
O C# segue a mesma ordem da matemática da escola: *, / e % (o resto da divisão) vêm antes de + e -.
Console.WriteLine(2 + 3 * 4); Console.WriteLine((2 + 3) * 4); Console.WriteLine(10 - 2 % 3); Console.WriteLine(-2 * -3);
$ dotnet run conta.cs 14 « 3 * 4 = 12 primeiro; depois 2 + 12 20 « os parênteses mandaram fazer 2 + 3 primeiro 8 « 2 % 3 = 2 primeiro (o resto de 2 dividido por 3); depois 10 - 2 6 « o sinal de menos "grudado" no número vem antes de tudo
- 2 + 3 * 4
- O
*tem prioridade maior que o+, então o C# calcula3 * 4mesmo ele estando à direita. Pense assim: o C# coloca parênteses invisíveis,2 + (3 * 4). - (2 + 3) * 4
- Os parênteses têm a palavra final: o que está dentro deles é calculado primeiro.
- 10 - 2 % 3
- O
%está no mesmo nível do*e do/(aula Operadores Aritméticos). - -2 * -3
- Um
-na frente de um valor, sem nada à esquerda, é o menos unário ("o oposto de"). Ele, o!e o(double)ficam no topo da fila: são feitos antes de qualquer conta.
2. Mesma prioridade: da esquerda para a direita
Quando os operadores têm a mesma prioridade, o C# vai da esquerda para a direita. Isso se chama associatividade à esquerda, e muda o resultado da subtração e da divisão:
Console.WriteLine(10 - 4 - 3); Console.WriteLine(100 / 10 / 2); Console.WriteLine(10 / 3 * 3); Console.WriteLine(10 * 3 / 3); int a; int b; a = b = 7; Console.WriteLine(a + " " + b);
$ dotnet run esquerda.cs 3 « (10 - 4) - 3, e não 10 - (4 - 3) = 9 5 « (100 / 10) / 2, e não 100 / (10 / 2) = 20 9 « (10 / 3) * 3 = 3 * 3: a divisão de inteiros já jogou fora o resto 10 « (10 * 3) / 3 = 30 / 3 7 7 « a = (b = 7): a atribuição vai da direita para a esquerda
As linhas 3 e 4 mostram que, com inteiros, até a ordem de * e / faz diferença: 10 / 3 dá 3 (sem a parte quebrada), e aí não tem volta.
A exceção é a atribuição, que vai da direita para a esquerda: em a = b = 7, primeiro b = 7 (que dá 7) e depois a = 7. Funciona, mas uma atribuição por linha é mais fácil de ler.
3. O erro clássico: a média
A Ana tirou 7 e 8. A média é 7,5. Veja o que acontece sem parênteses:
double nota1 = 7; double nota2 = 8; Console.WriteLine(nota1 + nota2 / 2); Console.WriteLine((nota1 + nota2) / 2);
$ dotnet run media.cs 11 « 7 + (8 / 2) = 7 + 4 7,5 « (7 + 8) / 2 = 15 / 2
O programa compila e roda, sem erro nem aviso, e mostra uma média de 11, maior que as duas notas. Erros de precedência são assim: o compilador não tem como saber o que você queria. Por isso, sempre que uma conta tiver soma e divisão juntas, pergunte-se: "qual parte vem primeiro?" e escreva os parênteses.
Missão: o boletim da Ana mostra a média errada. Ponha os parênteses para a média de 7 e 8 aparecer como Média: 7,5. Depois teste com três notas (lembre de dividir por 3).
Média: 7,5
double nota1 = 7;
double nota2 = 8;
double media = nota1 + nota2 / 2;
Console.WriteLine("Média: " + media);
double nota1 = 7;
double nota2 = 8;
double media = (nota1 + nota2) / 2;
Console.WriteLine("Média: " + media);
4. Quando o + junta textos
O + tem dois trabalhos: somar números e juntar textos (aula Strings). Quando um dos lados é texto, o outro vira texto e os dois são colados. Junte isso com a regra "da esquerda para a direita" e veja:
Console.WriteLine("Total: " + 2 + 3);
Console.WriteLine(2 + 3 + " itens");
Console.WriteLine("Total: " + (2 + 3));
Console.WriteLine("Dobro: " + 2 * 3);
$ dotnet run texto.cs
Total: 23 « ("Total: " + 2) + 3 = "Total: 2" + 3 = "Total: 23"
5 itens « (2 + 3) + " itens" = 5 + " itens"
Total: 5 « os parênteses fizeram a soma antes
Dobro: 6 « o * vem antes do +, então 2 * 3 foi feito primeiro
- Linha 1: o primeiro
+encontra um texto ("Total: ") e um número (2). Resultado: o texto"Total: 2". O segundo+encontra outro texto e o 3: cola de novo. Nenhuma soma foi feita. - Linha 2: agora os números vêm primeiro:
2 + 3são dois números, então é soma (5). Só depois aparece o texto. - Linha 4: o
*tem prioridade sobre o+, então2 * 3é calculado antes de encontrar o texto. Funciona, mas os parênteses deixam mais claro:"Dobro: " + (2 * 3).
O mesmo vale com as comparações (que ficam abaixo do + na fila): "Pode votar? " + idade >= 16 não compila, como você viu em Operadores de Comparação. A interpolação, $"Total: {2 + 3}", evita esse problema de vez (aula Interpolação).
5. O cast vem antes da divisão
O (double) (aula Conversão de Tipos) está no topo da fila, junto com o menos unário. Ele pega só o valor que vem logo depois dele:
int soma = 15; Console.WriteLine((double)soma / 2); Console.WriteLine((double)(soma / 2));
$ dotnet run cast.cs 7,5 « ((double)soma) / 2 = 15,0 / 2: divisão de double 7 « (double)(15 / 2) = (double)7: a divisão de inteiros já tinha jogado fora o ,5
Na primeira linha, o soma vira 15.0 antes da divisão, e a conta é feita com vírgula. Na segunda, os parênteses mandaram dividir primeiro (entre inteiros, 7) e só depois converter: tarde demais.
6. Comparações, && e || na fila
As contas vêm antes das comparações, que vêm antes do &&, que vem antes do ||. Por isso esta linha funciona sem nenhum parêntese:
int idade = 70; bool estudante = false; decimal total = 80m; bool desconto = estudante || idade >= 60 + 5 && total > 50m; Console.WriteLine(desconto); Console.WriteLine(!estudante == true);
$ dotnet run tudo.cs True True
Na linha 4, o C# lê assim, passo a passo:
60 + 5→65(conta primeiro).idade >= 65→trueetotal > 50m→true(comparações).true && true→true(o&&antes do||).estudante || true→true.
Na linha 6, o ! pega só o estudante (é unário, vem antes de tudo): (!estudante) == true. Funciona, mas ninguém quer fazer esse passo a passo para entender uma linha. Compare com a versão com parênteses: estudante || (idade >= 65 && total > 50m). Os detalhes de && × || estão em Operadores Lógicos.
A tabela completa
Do que é feito primeiro (no topo) para o que é feito por último. Os operadores da mesma linha têm a mesma prioridade. Você não precisa decorar: as linhas destacadas em negrito são as que aparecem todo dia.
| Nível | Operadores | Nome | Associatividade |
|---|---|---|---|
| 1 | x.y f(x) a[i] x?.y x++ x-- new typeof nameof | primários | → |
| 2 | -x +x !x ++x --x (T)x ~x ^x await | unários e cast | — |
| 3 | x..y | intervalo (Índices e Intervalos) | — |
| 4 | switch with | expressões switch e with | — |
| 5 | * / % | multiplicação, divisão, resto | → |
| 6 | + - | soma, subtração, juntar textos | → |
| 7 | << >> >>> | deslocamento de bits | → |
| 8 | < > <= >= is as | comparação e teste de tipo | → |
| 9 | == != | igualdade | → |
| 10 | & | E sem curto-circuito / de bits | → |
| 11 | ^ | OU exclusivo | → |
| 12 | | | OU sem curto-circuito / de bits | → |
| 13 | && | E | → |
| 14 | || | OU | → |
| 15 | ?? | "se for null, use…" (Operadores ??, ?. e ??=) | ← |
| 16 | c ? a : b | ternário (Operador Ternário) | ← |
| 17 | = += -= *= /= %= ??= … => | atribuição e lambda | ← |
"→" quer dizer "da esquerda para a direita" (10 - 4 - 3 = (10 - 4) - 3); "←", "da direita para a esquerda" (a = b = 7 = a = (b = 7)). Os operadores dos níveis 3, 4, 7, 10 a 12 e 15 aparecem mais tarde no guia; eles estão aqui para a tabela ficar completa.
unários (- ! (double)) → contas (* / %, depois + -) → comparações (< >, depois == !=) → lógicos (&&, depois ||) → atribuição (=). Fora isso, parênteses.
Formas diferentes: parênteses que "não precisam"
| Sem parênteses | Com parênteses | Qual usar |
|---|---|---|
a + b * c | a + (b * c) | Sem: todo mundo conhece a regra da escola. |
"Dobro: " + 2 * 3 | "Dobro: " + (2 * 3) | Com: texto e conta juntos confundem. |
x > 0 && y > 0 | (x > 0) && (y > 0) | Sem: as comparações já vêm antes do &&, e o código fica limpo. |
a || b && c | a || (b && c) | Com: pouca gente lembra que && vem antes, e o compilador do C# não avisa. |
(double)soma / n | ((double)soma) / n | Sem: é a forma que você vai ver em todo lugar. |
A regra prática: parênteses que deixam a intenção clara são bem-vindos, mesmo quando não mudam o resultado. Parênteses demais, em volta de cada pedaço, atrapalham a leitura. Quando a expressão ficar enorme, divida em variáveis com bons nomes: bool idoso = idade >= 65;.
Quando dá errado
O que significa: é o sintoma típico de precedência: nota1 + nota2 / 2 no lugar de (nota1 + nota2) / 2, ou "Total: " + 2 + 3 no lugar de "Total: " + (2 + 3).
Como consertar: faça a conta à mão, passo a passo, seguindo a tabela; onde o seu passo a passo for diferente do que você queria, ponha parênteses.
error CS0019: O operador "-" não pode ser aplicado a operandos dos tipos "string" e "int"
int compras = 10;
int devolvidas = 3;
Console.WriteLine("Ficaram: " + compras - devolvidas);
$ dotnet run ep1.cs /home/ana/cs/ep1.cs(3,19): error CS0019: O operador "-" não pode ser aplicado a operandos dos tipos "string" e "int" Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O que significa: + e - têm a mesma prioridade e vão da esquerda para a direita. O C# fez "Ficaram: " + compras (o texto "Ficaram: 10") e depois tentou tirar 3 de um texto. Com + no lugar do -, o erro seria pior, porque não haveria erro: apareceria Ficaram: 103.
Como consertar: "Ficaram: " + (compras - devolvidas).
error CS1026: ) esperado
int x = 5; int y = 3; Console.WriteLine((x + y) * (x - y);
$ dotnet run ep2.cs /home/ana/cs/ep2.cs(3,36): error CS1026: ) esperado Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O que significa: há mais parênteses abrindo do que fechando. Conte: WriteLine(, (x e (x abrem três; y) e y) fecham dois. A coluna 36 é onde o C# esperava o que faltou, logo antes do ;.
Como consertar: feche o que abriu: Console.WriteLine((x + y) * (x - y));. Muitos editores pintam o par de cada parêntese quando o cursor está nele.
Exercícios
1. Faça a conta
Quanto vale 4 + 6 / 2?
2. Da esquerda para a direita
Quanto vale 20 - 5 - 5?
3. Texto e números
O que Console.WriteLine(1 + 1 + "1" + 1); mostra?
4. Com int soma = 9;, quanto vale (double)(soma / 2)?
5. Qual é calculado primeiro em a || b && c?
6. Por que "Ficaram: " + 10 - 3 não compila?
Missão final: o total do pedido
Missão: o pedido da lojinha tem 2 cadernos de R$ 12,50 e R$ 5,00 de frete. O programa mostra Total: 25,005, um número sem sentido. Descubra o que o C# fez (siga a tabela!) e ponha os parênteses para aparecer Total: 30,00.
Total: 30,00
decimal preco = 12.50m;
int quantidade = 2;
decimal frete = 5m;
Console.WriteLine("Total: " + preco * quantidade + frete);
decimal preco = 12.50m;
int quantidade = 2;
decimal frete = 5m;
Console.WriteLine("Total: " + (preco * quantidade + frete));
A explicação: o * foi feito primeiro (25,00, com as duas casas do 12.50m), o primeiro + colou esse número no texto ("Total: 25,00") e o segundo + colou o frete (5) no fim. Em Formatar Números você vai aprender a mostrar R$ 30,00 do jeito certo.
Resumo
- Precedência é a ordem dos operadores: unários e cast →
* / %→+ -→ comparações →== !=→&&→||→?:→ atribuição. - Associatividade: na mesma prioridade, da esquerda para a direita (
10 - 4 - 3= 3). A atribuição vai da direita para a esquerda. - A média precisa de parênteses:
(nota1 + nota2) / 2. - Com texto, o
+cola:"Total: " + 2 + 3dáTotal: 23. Use"Total: " + (2 + 3)ou a interpolação. - O cast pega só o que vem depois:
(double)soma / 2dá 7,5;(double)(soma / 2)dá 7. - Erros de precedência quase nunca dão erro de compilação: o resultado só fica errado. Na dúvida, parênteses.