C# Operadores

Operadores são os símbolos que fazem contas e comparações: +, ==, &&, ??… Esta página traz todos os operadores do C# 14 em tabelas: primeiro a precedência (quem é calculado primeiro numa expressão com vários), depois cada grupo, com exemplo e resultado. Para aprender com calma, comece por Operadores Aritméticos e Precedência.

Todos os resultados vêm dos programas desta página, rodados no dotnet 10.0.112 da máquina da Ana (pasta ~/cs/op, cultura pt-BR) e no editor do guia, que deram a mesma saída.

Atalhos: Precedência · Aritméticos · Atribuição · ++ e -- · Comparação · Lógicos · Bits · null · Tipos · Índices e intervalos · Outros · Armadilhas · Experimente


Tabela de precedência e associatividade

Numa expressão com vários operadores, os de cima da tabela são calculados antes dos de baixo, como a multiplicação antes da soma na escola. Na mesma linha, a associatividade decide: "esquerda → direita" quer dizer que 10 - 4 - 3 é (10 - 4) - 3; "direita → esquerda" quer dizer que a = b = 7 é a = (b = 7).

#GrupoOperadoresAssociatividade
1Primáriosx.y f(x) a[i] x?.y x?[i] x++ x-- x! new typeof checked unchecked default nameof delegate sizeof stackalloc x->yesquerda → direita
2Unários+x -x !x ~x ++x --x ^x (T)x await &x *x true falsedireita → esquerda
3Intervalox..y(não se encadeia)
4switch e withx switch { … } x with { … }esquerda → direita
5Multiplicativosx * y x / y x % yesquerda → direita
6Aditivosx + y x - yesquerda → direita
7Deslocamentox << y x >> y x >>> yesquerda → direita
8Relacionais e de tipox < y x > y x <= y x >= y is asesquerda → direita
9Igualdadex == y x != yesquerda → direita
10E (lógico ou de bits)x & yesquerda → direita
11OU exclusivox ^ yesquerda → direita
12OU (lógico ou de bits)x | yesquerda → direita
13E condicionalx && yesquerda → direita
14OU condicionalx || yesquerda → direita
15Coalescência nulax ?? ydireita → esquerda
16Condicional (ternário)c ? a : bdireita → esquerda
17Atribuição e lambda= += -= *= /= %= &= |= ^= <<= >>= >>>= ??= =>direita → esquerda

Veja a tabela funcionando. Cada linha mostra a expressão e o resultado:

// precedencia.cs — quem é calculado primeiro
int a, b;
string? nada = null, vazio = null;

Console.WriteLine($"2 + 3 * 4       = {2 + 3 * 4}");
Console.WriteLine($"(2 + 3) * 4     = {(2 + 3) * 4}");
Console.WriteLine($"10 - 4 - 3      = {10 - 4 - 3}");
Console.WriteLine($"100 / 10 / 5    = {100 / 10 / 5}");
Console.WriteLine($"2 * 3 % 4       = {2 * 3 % 4}");
Console.WriteLine($"-2 * -3         = {-2 * -3}");
Console.WriteLine($"1 << 2 + 1      = {1 << 2 + 1}");
Console.WriteLine($"8 > 5 == true   = {8 > 5 == true}");
Console.WriteLine($"true || false && false = {true || false && false}");
Console.WriteLine($"(true || false) && false = {(true || false) && false}");
Console.WriteLine($"6 & 3 | 8       = {6 & 3 | 8}");
Console.WriteLine($"nada ?? vazio ?? \"padrão\" = {nada ?? vazio ?? "padrão"}");
Console.WriteLine($"5 > 3 ? \"sim\" : \"não\" = {(5 > 3 ? "sim" : "não")}");
a = b = 7;
Console.WriteLine($"a = b = 7       → a = {a}, b = {b}");
a = 10;
a += b *= 2;
Console.WriteLine($"a += b *= 2     → a = {a}, b = {b}");
int i = 5;
int r = i++ + ++i;
Console.WriteLine($"i++ + ++i (i=5) → {r}, e i = {i}");
int[] v = [10, 20, 30];
Console.WriteLine($"v[^1] * 2       = {v[^1] * 2}");
Console.WriteLine($"-v[0] + v[1]    = {-v[0] + v[1]}");
$ dotnet run precedencia.cs
2 + 3 * 4       = 14
(2 + 3) * 4     = 20
10 - 4 - 3      = 3
100 / 10 / 5    = 2
2 * 3 % 4       = 2
-2 * -3         = 6
1 << 2 + 1      = 8
8 > 5 == true   = True
true || false && false = True
(true || false) && false = False
6 & 3 | 8       = 10
nada ?? vazio ?? "padrão" = padrão
5 > 3 ? "sim" : "não" = sim
a = b = 7       → a = 7, b = 7
a += b *= 2     → a = 24, b = 14
i++ + ++i (i=5) → 12, e i = 7
v[^1] * 2       = 60
-v[0] + v[1]    = 10
ExpressãoÉ lida comoResultadoRegra
2 + 3 * 42 + (3 * 4)14* (5) antes de + (6)
10 - 4 - 3(10 - 4) - 33esquerda → direita
100 / 10 / 5(100 / 10) / 52esquerda → direita (não é 100 / 2)
2 * 3 % 4(2 * 3) % 42* e % estão na mesma linha: esquerda → direita
1 << 2 + 11 << (2 + 1)8+ (6) antes de << (7)
8 > 5 == true(8 > 5) == trueTrue> (8) antes de == (9)
true || false && falsetrue || (false && false)True&& (13) antes de || (14)
6 & 3 | 8(6 & 3) | 810& (10) antes de | (12)
nada ?? vazio ?? "padrão"nada ?? (vazio ?? "padrão")padrãodireita → esquerda: o primeiro que não é null
a = b = 7a = (b = 7)os dois valem 7direita → esquerda: a atribuição devolve o valor atribuído
a += b *= 2a += (b *= 2)a = 24, b = 14direita → esquerda. Funciona, mas não escreva assim
i++ + ++i (com i = 5)5 + 712os operandos são calculados da esquerda para a direita: i++ dá 5 (e i vira 6), ++i dá 7. No C# isso é garantido; mesmo assim, evite
-v[0] + v[1](-(v[0])) + v[1]10v[0] (1) antes do - unário (2)
Na dúvida, parênteses

Ninguém decora as 17 linhas. Os parênteses não custam nada e deixam a intenção clara para quem lê: (a && b) || c é melhor que a && b || c, mesmo que os dois sejam iguais. Basta lembrar três coisas: * / % antes de + -; comparações antes de && e ||; e a atribuição por último. Aula: Precedência.


As tabelas de grupo a seguir usam este programa. Cada linha da saída corresponde a uma tabela:

// opers.cs — um exemplo de cada grupo
int x = 17, y = 5;
Console.WriteLine($"{x + y} {x - y} {x * y} {x / y} {x % y} {-x}");
Console.WriteLine($"{17.0 / 5} {17m / 5} {-17 / 5} {-17 % 5}");
int c = 10;
c += 5; Console.Write($"{c} ");
c -= 3; Console.Write($"{c} ");
c *= 2; Console.Write($"{c} ");
c /= 4; Console.Write($"{c} ");
c %= 4; Console.WriteLine(c);
int n = 5;
Console.WriteLine($"{n++} {n} {++n} {n--} {--n}");
Console.WriteLine($"{x == y} {x != y} {x < y} {x > y} {x <= 17} {x >= 18}");
bool t = true, f = false;
Console.WriteLine($"{t && f} {t || f} {!t} {t & f} {t | f} {t ^ f} {t ^ t}");
Console.WriteLine($"{6 & 3} {6 | 3} {6 ^ 3} {~6} {1 << 4} {-16 >> 2} {-16 >>> 28}");
string? nome = null;
Console.WriteLine($"[{nome?.Length}] [{nome ?? "sem nome"}] [{nome?.ToUpper() ?? "?"}]");
nome ??= "Ana";
Console.WriteLine($"{nome} {nome!.Length}");
object o = "texto";
Console.WriteLine($"{o is string} {o is int} {(o as string)?.Length} {typeof(string).Name} {sizeof(long)} {nameof(nome)}");
Console.WriteLine($"{(int)3.7} {(double)7 / 2} {default(int)}");
int[] v = [10, 20, 30, 40, 50];
Console.WriteLine($"{v[^1]} {v[^2]} {string.Join(",", v[1..3])} {string.Join(",", v[..2])} {string.Join(",", v[3..])}");
Func<int, int> dobro = k => k * 2;
Console.WriteLine(dobro(21));
string tamanho = x switch { < 10 => "pequeno", < 100 => "médio", _ => "grande" };
Console.WriteLine(tamanho);
$ dotnet run opers.cs
22 12 85 3 2 -17
3,4 3,4 -3 -2
15 12 24 6 2
5 6 7 7 5
False True False True True False
False True False False True True False
2 7 5 -7 16 -4 15
[] [sem nome] [?]
Ana 3
True False 5 String 8 nome
3 3,5 0
50 40 20,30 10,20 40,50
42
médio

Aritméticos

OperadorNomeExemplo (x = 17, y = 5)ResultadoAula
+soma (e junta textos)x + y22Operadores Aritméticos
-subtração; com um operando só, troca o sinalx - y · -x12 · -17
*multiplicaçãox * y85
/divisão. Entre inteiros, corta a parte decimalx / y · 17.0 / 5 · 17m / 5 · -17 / 53 · 3,4 · 3,4 · -3
%resto da divisão (com o sinal do primeiro)x % y · -17 % 52 · -2
+xmais unário (não muda nada)+x17Operadores Aritméticos

Inteiro dividido por zero lança DivideByZeroException; double dividido por zero dá ∞ ou NaN. Detalhes em Tipos e Limites.

Atribuição

OperadorÉ o mesmo queExemplo (começando com c = 10)ResultadoAula
=guarda o valorc = 1010Variáveis
+=c = c + 5c += 515Operadores de Atribuição
-=c = c - 3c -= 312
*=c = c * 2c *= 224
/=c = c / 4c /= 46
%=c = c % 4c %= 42
&= |= ^= <<= >>= >>>=c = c & … etc.flags |= 4liga o bit 4Operadores de Atribuição
??=if (c is null) c = …nome ??= "Ana"só atribui se for nullOperadores ??, ?. e ??=

++ e --

FormaO que fazExemplo (em sequência, começando com n = 5)MostraDepois, n vale
n++usa o valor, depois soma 1n++56
++nsoma 1, depois usa++n77
n--usa, depois subtrai 1n--76
--nsubtrai 1, depois usa--n55

Sozinho numa linha (n++;), tanto faz a forma. A diferença só aparece quando o valor é usado na mesma expressão. Aula: ++ e --.

Comparação

OperadorPerguntaExemplo (x = 17, y = 5)ResultadoAula
==é igual?x == yFalseOperadores de Comparação
!=é diferente?x != yTrue
<é menor?x < yFalse
>é maior?x > yTrue
<=é menor ou igual?x <= 17True
>=é maior ou igual?x >= 18False

Para textos, == compara o conteúdo ("ana" == "Ana" é False); para ignorar maiúsculas, string.Equals(a, b, StringComparison.OrdinalIgnoreCase) (veja string e StringBuilder). Para objetos de classe, == compara se é o mesmo objeto, a não ser que a classe defina outra coisa (records comparam os campos). Aula: A Classe object.

Lógicos (com bool)

OperadorNomeVerdadeiro quando…Exemplo (t = true, f = false)ResultadoAula
&&Eos dois são verdadeiros. Se o primeiro é falso, nem olha o segundot && fFalseOperadores Lógicos
||OUpelo menos um é verdadeiro. Se o primeiro é verdadeiro, nem olha o segundot || fTrue
!NÃOo valor é falso!tFalse
&E sem atalhocomo &&, mas sempre calcula os dois ladost & fFalseOperadores Lógicos
|OU sem atalhocomo ||, sempre calculando os doist | fTrue
^OU exclusivoexatamente um é verdadeirot ^ f · t ^ tTrue · False

O atalho do && é o que torna seguro escrever texto != null && texto.Length > 0: se texto é null, o Length nem é lido.

Bits (com inteiros)

OperadorNomeExemploEm binárioResultado
&E bit a bit6 & 3110 & 011 = 0102
|OU bit a bit6 | 3110 | 011 = 1117
^OU exclusivo bit a bit6 ^ 3110 ^ 011 = 1015
~inverte todos os bits~6(complemento de dois)-7
<<desloca para a esquerda (× 2 a cada casa)1 << 41 → 1000016
>>desloca para a direita, mantendo o sinal-16 >> 2—-4
>>> C# 11desloca para a direita, enchendo com zeros-16 >>> 28—15

Os operadores de bits aparecem em enums com [Flags], em permissões e em formatos de arquivo. Para o dia a dia, os lógicos com bool bastam.

Operadores de null

OperadorO que fazExemplo (nome = null)ResultadoAula
?.acessa o membro só se não for null; senão, dá nullnome?.Lengthnull (mostrado vazio)Operadores ??, ?. e ??=
?[]o mesmo para índiceslista?[0]null se lista for null
??usa o da direita se o da esquerda for nullnome ?? "sem nome" · nome?.ToUpper() ?? "?"sem nome · ?
??=atribui só se for nullnome ??= "Ana"nome vira "Ana"
x!"confie em mim, não é null": só cala o aviso do compilador, não muda nada ao rodarnome!.Length3Referências Anuláveis
x?.y = valor C# 14atribui só se x não for nullcliente?.Nome = "Ana";veja abaixoOperadores ??, ?. e ??=

A atribuição com ?. é nova no C# 14 (antes, só dava para ler com ?.):

// C# 14: atribuição com ?.
Cliente? semCliente = null;
Cliente? cliente = new();
semCliente?.Nome = "Bruno";     // não faz nada: semCliente é null
cliente?.Nome = "Ana";          // atribui: cliente não é null
Console.WriteLine($"[{semCliente?.Nome}] [{cliente?.Nome}]");

class Cliente { public string Nome { get; set; } = ""; }
$ dotnet run nullassign.cs
[] [Ana]

Operadores de tipo e conversão

OperadorO que fazExemplo (object o = "texto")ResultadoAula
istesta o tipo (ou um padrão)o is string · o is intTrue · FalsePattern Matching
asconverte, ou dá null se não der (só tipos de referência e anuláveis)(o as string)?.Length5Polimorfismo
(T)xcast: converte, ou lança InvalidCastException(int)3.7 · (double)7 / 23 · 3,5Conversão de Tipos
typeof(T)o Type de um tipotypeof(string).NameStringReflexão
sizeof(T)bytes de um tipo de valorsizeof(long)8Tipos e Limites
nameof(x)o nome, como textonameof(nome)nomethrow
defaulto valor padrãodefault(int)0Tipos e Limites

Repare no (double)7 / 2: o cast (linha 2 da precedência) vem antes da divisão (linha 5). Primeiro o 7 vira 7.0, depois divide, e o resultado é 3,5. Já (double)(7 / 2) daria 3: a divisão de inteiros acontece antes.

Índices e intervalos

OperadorO que fazExemplo (v = [10, 20, 30, 40, 50])ResultadoAula
a[i]o item da posição i (do 0)v[0]10Arrays
^n C# 8contando do fim: ^1 é o últimov[^1] · v[^2]50 · 40Índices e Intervalos
a..b C# 8do a (incluído) ao b (fora)v[1..3] · v[..2] · v[3..]20,30 · 10,20 · 40,50

Outros operadores

OperadorO que fazExemploAula
x.yacessa um membronome.LengthClasses e Objetos
f(x)chama um métodoMath.Max(3, 7)Métodos
newcria um objetonew List<int>() · new()Construtores
c ? a : b"se c, então a, senão b"idade >= 18 ? "maior" : "menor"Operador Ternário
=>lambda (função sem nome) ou corpo de expressãok => k * 2 (com 21 dá 42)Lambdas
x switch { … } C# 8escolhe um valor pelo padrão que casarx switch { < 10 => "pequeno", < 100 => "médio", _ => "grande" } (com 17 dá médio)Expressões switch
x with { … } C# 9cópia de um record ou struct com mudançasp with { Preco = 9.90m }Records
awaitespera uma Task sem travarawait Task.Delay(1000)async e await
checked(…), unchecked(…)estouro vira exceção, ou dá a voltachecked(a * b)Inteiros
+ com textojunta (concatena)"Olá, " + nomeStrings
&x, *p, p->yendereço e ponteiro (só em código unsafe)int* p = &x;Memória

Suas classes podem definir o que +, ==, < e outros fazem com elas: aula Sobrecarga de Operadores.


Armadilhas

Texto + número: a soma que vira emenda
string nome = "Ana";
int idade = 29;
Console.WriteLine("Idade: " + idade + 1);
Console.WriteLine("Idade: " + (idade + 1));
Console.WriteLine(1 + 2 + " anos");
Console.WriteLine(nome + 1 + 2);
$ dotnet run e4.cs
Idade: 291
Idade: 30
3 anos
Ana12

O que aconteceu: + é calculado da esquerda para a direita. Em "Idade: " + idade + 1, primeiro vem "Idade: " + 29, que já é texto ("Idade: 29"); depois, texto + 1 emenda o 1. Em 1 + 2 + " anos", a conta 1 + 2 acontece antes de aparecer o texto.

Como consertar: parênteses na conta, (idade + 1), ou, melhor ainda, interpolação: $"Idade: {idade + 1}" (aula Interpolação).

CódigoMensagem real (lab)O que aconteceu e como consertarAula
if (idade = 18)error CS0029: Não é possível converter implicitamente tipo "int" em "bool"= guarda; == compara. Escreva if (idade == 18). No C#, o erro aparece na compilação (em C, compilaria).Comparação
if (permissoes & 4 == 4)error CS0019: O operador "&" não pode ser aplicado a operandos dos tipos "int" e "bool"O == (linha 9) vem antes do & (linha 10): virou permissoes & (4 == 4). Escreva (permissoes & 4) == 4.Precedência
if (nota >= 0 && <= 10)error CS1525: Termo de expressão inválido "<="Cada lado do && é uma comparação completa: nota >= 0 && nota <= 10. Ou, com padrões: nota is >= 0 and <= 10.Pattern Matching
int media = prova1 + prova2 / 2;(compila, e a média sai errada)A divisão vem antes da soma. Escreva (prova1 + prova2) / 2. Veja a missão abaixo.Precedência
7 / 2 esperando 3,5(compila e dá 3)Entre inteiros, a divisão corta. Use 7.0 / 2, 7m / 2 ou (double)7 / 2.Aritméticos
0.1 + 0.2 == 0.3(compila e dá False)O double não guarda 0,1 exato. Compare com tolerância, ou use decimal.float, double e decimal

Experimente

Missão: a Ana tirou 7 e 9 nas provas, mas o programa diz que a média é 11. Conserte a conta (sem mudar os números) para sair Média: 8.

int prova1 = 7;
int prova2 = 9;
int media = prova1 + prova2 / 2;
Console.WriteLine($"Média: {media}");
int prova1 = 7;
int prova2 = 9;
int media = (prova1 + prova2) / 2;
Console.WriteLine($"Média: {media}");

Experimente: tente prever cada resultado antes de rodar. Depois, tire e ponha parênteses e veja o que muda.

int a = 2, b = 3, c = 4;
Console.WriteLine(a + b * c);
Console.WriteLine((a + b) * c);
Console.WriteLine(a * b % c);
Console.WriteLine(a < b == b < c);
Console.WriteLine(a > b || b < c && c > 10);
Console.WriteLine("Total: " + a + b);
Console.WriteLine($"Total: {a + b}");
string? apelido = null;
Console.WriteLine(apelido ?? "sem apelido");

Resumo

  • São 17 níveis de precedência. O essencial: primários (x.y, f(), a[i], x++), unários (-x, !x, cast), * / %, + -, comparações, ==, &&, ||, ??, ?: e, por último, a atribuição e o =>.
  • Quase todos vão da esquerda para a direita; a atribuição, o ?? e o ?: vão da direita para a esquerda.
  • Divisão entre inteiros corta; % é o resto.
  • && e || param assim que sabem a resposta; & e | calculam os dois lados.
  • ?., ?? e ??= tratam o null; no C# 14, x?.y = valor também atribui.
  • Na dúvida, parênteses.