Menu

Operadores em C#: aritméticos, módulo, lógicos, bit a bit e precedência

Os operadores de C# com o comportamento exato: divisão inteira, o operador de resto com números negativos, incremento e atribuição composta, comparação, lógica com curto-circuito, operadores bit a bit, os operadores de null e uma tabela de precedência.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Um operador recebe um, dois ou três operandos e produz um valor: a + b, !done, x ?? fallback. Os operadores de C# são próximos dos de C, Java e JavaScript, mas alguns se comportam de formas que surpreendem quem está começando, principalmente a divisão inteira e o sinal do %.

Operadores aritméticos

+, -, *, / e % funcionam em todo tipo numérico. O tipo do resultado segue os operandos: dois valores int produzem um int, um int e um double produzem um double.

Saída:

22
12
85
3
2
3.4
3.4
3
3.4

A divisão inteira trunca em direção a zero. 17 / 5 é 3 e -17 / 5 é -3. Isso causa um bug muito comum: int percent = done / total * 100; é 0 sempre que done < total, porque done / total já é 0. Multiplique primeiro (done * 100 / total) ou divida como double.

Dividir um inteiro ou um decimal por zero lança DivideByZeroException. Dividir um double por zero não lança nada; produz infinito, ou NaN para 0.0 / 0.0:

Saída:

True
True
False
DivideByZeroException

O operador de resto e os números negativos

% retorna o resto da divisão, e o resultado tem o sinal do operando da esquerda. Isso difere do Python e do "mod" matemático, que são sempre não negativos para um divisor positivo:

Saída:

1
-1
1
2
even
62 min 5 s
Thu
1.5

Use o auxiliar Mod sempre que o lado esquerdo puder ser negativo e o resultado for um índice: days[(today + offset) % 7] lançaria uma IndexOutOfRangeException para uma soma negativa. Testar números ímpares tem a mesma armadilha: n % 2 == 1 é false para ímpares negativos; teste n % 2 != 0.

Atribuição, atribuição composta e incremento

= atribui. As formas compostas +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>= combinam uma operação com a atribuição. ++ e -- somam ou subtraem 1 e existem nas formas prefixa e pós-fixa, que diferem no valor que retornam:

Saída:

14
start, step
a=5 b=7 i=7
4

O caso do byte mostra uma peculiaridade: level = level + 10; não compila, porque byte + int é um int, mas level += 10; compila, porque a atribuição composta inclui um cast implícito de volta para o tipo da variável. Esse cast dá a volta no overflow (250 + 10 vira 4).

Operadores de comparação e lógicos

Os operadores de comparação (==, !=, <, >, <=, >=) retornam um bool. Para strings, == compara o texto, não as referências. Os operadores lógicos são ! (não), && (e), || (ou) e ^ (ou exclusivo).

Saída:

True
no active user
with ||:
  evaluated A
with |:
  evaluated A
  evaluated B
True

&& e || fazem curto-circuito: o operando da direita só executa quando pode mudar o resultado. É isso que torna user != null && user.IsActive seguro. Os operadores simples & e | em booleanos sempre avaliam os dois lados, o que raramente é o que você quer em uma condição.

Operadores bit a bit e de deslocamento

Em tipos inteiros, &, |, ^ e ~ trabalham em bits individuais, e << e >> os deslocam. Eles são usados em flags, máscaras, hashing e protocolos de baixo nível:

Saída:

3
True
False
101
1024
125
5
-1

Para conjuntos de flags com nome, um enum com o atributo [Flags] é mais claro que constantes inteiras, e HasFlag se lê melhor que uma máscara. O C# 11 adicionou >>>, um deslocamento à direita sem sinal que preenche com zeros mesmo para números negativos.

Operadores de null

Três operadores existem para trabalhar com valores que podem ser null:

Saída:

Guest

0
5

a ?? b retorna a, a menos que seja null, e então b. a?.Member retorna null em vez de lançar exceção quando a é null. O C# 8 adicionou a ??= b, que atribui b a a só quando a é null. Eles são cobertos em detalhe em tipos anuláveis.

Precedência dos operadores

Quando uma expressão mistura operadores, as linhas de cima se ligam com mais força. Operadores na mesma linha se agrupam da esquerda para a direita, exceto a atribuição, ?? e ?:, que se agrupam da direita para a esquerda. (Os operandos em si sempre são avaliados da esquerda para a direita.)

CategoriaOperadores
Primáriosx.y, x?.y, f(x), a[i], x++, x--, new, typeof, nameof
Unários+x, -x, !x, ~x, ++x, --x, (T)x, await
Multiplicativos*, /, %
Aditivos+, -
Deslocamento<<, >>, >>>
Relacionais e teste de tipo<, >, <=, >=, is, as
Igualdade==, !=
E bit a bit&
OU exclusivo bit a bit^
OU bit a bit|
E condicional&&
OU condicional||
Coalescência de null??
Condicionalc ? a : b
Atribuição e lambda=, +=, -=, ??=, => e as outras formas compostas

Duas consequências que vale conhecer: a + b * c multiplica primeiro, e x & mask == 0 compara primeiro (== se liga com mais força que &), então significa x & (mask == 0) e não compila para inteiros. Escreva (x & mask) == 0. Parênteses não custam nada e deixam a intenção óbvia.

Sobrecarga de operadores

Seus próprios tipos podem definir o que os operadores significam. É assim que decimal, DateTime e TimeSpan aceitam + e <:

Saída:

33.49
True

Os operadores precisam ser public static, e alguns vêm em pares obrigatórios: definir < exige >, e == exige !=. Com == definido, o compilador também avisa até você sobrescrever Equals e GetHashCode para combinar. Sobrecarregue operadores só onde o significado é óbvio, como em números, vetores e dinheiro.

Perguntas frequentes

O que o % faz em C#?

% é o operador de resto: 17 % 5 é 2. O resultado tem o sinal do operando da esquerda, então -7 % 3 é -1, não 2 como em Python. Para um resultado sempre não negativo, como você precisa ao dar a volta em um índice, escreva ((a % n) + n) % n. Ele também funciona com double e decimal: 7.5 % 2 é 1.5.

Por que 7 / 2 é igual a 3 em C#?

Quando os dois operandos são inteiros, / faz divisão inteira e descarta a parte fracionária, truncando em direção a zero. Faça um dos operandos ser de ponto flutuante ou decimal para obter um resultado fracionário: 7 / 2.0 é 3.5, (double)a / b converte antes de dividir, e 7m / 2 é 3.5 como decimal.

Qual a diferença entre && e & em C#?

&& é o E condicional: se o lado esquerdo for false, o lado direito não é avaliado. & em booleanos sempre avalia os dois lados, e em inteiros é o E bit a bit. Use && e || em condições, principalmente em guardas como user != null && user.IsActive, que lançaria uma NullReferenceException com &.

Qual a diferença entre i++ e ++i em C#?

Os dois somam 1 a i. A expressão i++ resulta no valor antigo, ++i no valor novo. Em uma linha sozinha (i++;) não há diferença. Só importa quando o resultado é usado, como em int a = i++;, e é por isso que muitos guias de estilo mantêm os incrementos em uma linha própria.

Qual é a precedência dos operadores em C#?

Da maior para a menor: primários (x.y, f(x), a[i], x++), unários (!, -, ++x, casts), multiplicativos (* / %), aditivos (+ -), deslocamentos, relacionais (< > <= >= is as), igualdade (== !=), &, ^, |, &&, ||, ??, o condicional ?: e, por fim, a atribuição. Na dúvida, adicione parênteses.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR