Operadores C#

Sugestão

Este artigo faz parte da secção Fundamentos para programadores que já conhecem pelo menos uma linguagem de programação e estão a aprender C#. Se és novo na programação, começa primeiro pelos tutoriais para começar .

Vem de outra língua? A maioria dos operadores neste artigo (+, -, *, /, %, &&, , ||, ==!<!=>operadores de comparação, e =) funciona da mesma forma que em Java, C++ e JavaScript. As principais surpresas para os iniciantes são o comportamento da divisão de inteiros, a distinção entre prefixo e sufixo para ++/-- e a forma como as atribuições compostas convertem novamente para o tipo do lado esquerdo.

Um operador combina um ou mais operandos num único valor. Já conhece as expressões e a precedência de operadores em expressões C#; este artigo analisa mais aprofundadamente os operadores específicos que utiliza todos os dias.

Operadores aritméticos

Os cinco operadores aritméticos realizam cálculos numéricos.

Operador Name Example Result
+ Adição 10 + 3 13
- Subtração 10 - 3 7
* Multiplicação 10 * 3 30
/ Divisão 10 / 3 3
% Remanescente 10 % 3 1
int apples = 10;
int oranges = 3;

Console.WriteLine(apples + oranges);  // => 13  (addition)
Console.WriteLine(apples - oranges);  // => 7   (subtraction)
Console.WriteLine(apples * oranges);  // => 30  (multiplication)
Console.WriteLine(apples / oranges);  // => 3   (integer division: truncates toward zero)
Console.WriteLine(apples % oranges);  // => 1   (remainder)

// Integer division always truncates toward zero — the fractional part is discarded
int result = 7 / 2;
Console.WriteLine(result); // => 3, not 3.5

// Truncation applies to negative results too: -7 / 2 is -3, not -4
int negResult = -7 / 2;
Console.WriteLine(negResult); // => -3

// To get a decimal result, at least one operand must be a double or float
double precise = 7.0 / 2;
Console.WriteLine(precise); // => 3.5

// Remainder with negative operands: the sign of the result matches the dividend
Console.WriteLine(-7 % 3);  // => -1  (-7 = 3 × -2 + (-1))
Console.WriteLine(7 % -3);  // => 1   ( 7 = -3 × -2 + 1)

A divisão inteira trunca em direção a zero. Quando ambos os operandos são inteiros, / descarta a parte fracionária: 7 / 2 é 3, não 3.5. O truncamento é para zero, não para o número menor: -7 / 2 é -3 (não -4). Para obter um resultado decimal, faça com que pelo menos um dos operandos seja de tipo de vírgula flutuante: 7.0 / 2 é 3.5. Isto difere de algumas linguagens onde / produz sempre um resultado em ponto flutuante.

O resto (%) devolve o que sobra após a divisão de inteiros: 10 % 3 é 1 porque 10 = 3 × 3 + 1. É útil para percorrer um intervalo fixo (index % length), testar a divisibilidade (n % 2 == 0) e extrair dígitos. Com operandos negativos, o sinal do resultado corresponde ao sinal do dividendo (o operando esquerdo): -7 % 3 é -1 e 7 % -3 é 1.

Operadores unários

Operadores unários atuam sobre um único operando.

int temperature = 20;
int windChill = -5;

int heatIndex = +temperature;   // unary +: value unchanged (rarely needed)
int coldFactor = -windChill;    // unary -: negates the value → 5

Console.WriteLine(heatIndex);   // => 20
Console.WriteLine(coldFactor);  // => 5

bool isRaining = false;
bool isSunny = !isRaining;     // logical NOT: flips true/false
Console.WriteLine(isSunny);    // => True
  • +x (unário mais) — mantém o valor inalterado; raramente escrito explicitamente, mas válido.
  • -x (unário menos) — anula o valor.
  • !x (NOT lógico) — muda true para false e false para true. Vais usar ! frequentemente: if (!list.Contains(item)).

Incremento e decréscimo

++ soma 1 e -- subtrai 1. Ambos têm uma forma de prefixo e uma forma de pós-fixo que diferem em qual valor é devolvido:

int counter = 5;

// Prefix: increment first, then use the new value
int a = ++counter;
Console.WriteLine(a);       // => 6
Console.WriteLine(counter); // => 6

// Postfix: use the current value first, then increment
int b = counter++;
Console.WriteLine(b);       // => 6  (value before increment)
Console.WriteLine(counter); // => 7  (incremented after)

// Decrement works the same way
int score = 10;
Console.WriteLine(score--); // => 10 (current value; score becomes 9)
Console.WriteLine(score);   // => 9
  • Prefixo (++i, --i): incrementa ou diminui a variável primeiro, depois devolve o novo valor.
  • Postfixo (i++, i--): devolve primeiro o valor atual , depois incrementa ou diminui a variável.

Quando ++ ou -- aparece como uma afirmação autónoma (não parte de uma expressão maior), prefixo e pós-fixo têm o mesmo efeito. A distinção só importa quando o resultado é usado — por exemplo, numa tarefa ou como argumento de método.

Operadores relacionais

Operadores relacionais comparam dois valores e retornam um bool.

Operador Meaning Example
< Menos de speed < limit
> Maior que speed > limit
<= Menor ou igual score <= 100
>= Maior ou igual score >= 0
int speed = 75;
int limit = 60;

Console.WriteLine(speed > limit);   // => True   (greater than)
Console.WriteLine(speed < limit);   // => False  (less than)
Console.WriteLine(speed >= limit);  // => True   (greater than or equal)
Console.WriteLine(speed <= limit);  // => False  (less than or equal)

// Relational operators work on all numeric types and char
// char comparison uses the character's numeric Unicode code point, not alphabetical position
// 'B' (U+0042, value 66) is less than 'A' (U+0041, value 65)? No — 'A' (65) < 'B' (66)
char grade = 'B';
Console.WriteLine(grade >= 'A' && grade <= 'C'); // => True  ('A'=65 <= 'B'=66 <= 'C'=67)

Os operadores relacionais trabalham em todos os tipos numéricos e char. Para char, a comparação utiliza o valor numérico do ponto de código Unicode do carácter, e não qualquer ordenação alfabética ou específica do domínio. No exemplo de classificação acima, 'B' é maior ou igual a 'A' porque 'B' tem valor Unicode 66 e 'A' valor Unicode 65 — os números determinam a comparação, não o significado das notas das letras.

Operadores de igualdade

== e != verificar se dois valores são iguais ou não. != é true quando os operandos não são iguais, e false quando são.

int expected = 42;
int actual = 42;

Console.WriteLine(actual == expected);  // => True   (values are equal)
Console.WriteLine(actual != expected);  // => False  (true when values are not equal)

string name = "Alice";
Console.WriteLine(name == "Alice");  // => True   (string content matches)
Console.WriteLine(name == "alice");  // => False  (case-sensitive)

int x = 5;
Console.WriteLine(x == 10);  // => False

Para tipos numéricos e string, a igualdade testa os valores. Para tipos de referência, o padrão é identidade (se duas variáveis apontam para o mesmo objeto), mas muitos tipos incluem string e record sobrepõem isto para comparar conteúdo. Para uma visão completa — como a igualdade funciona entre tipos de valores, tipos de referência, registos e estruturas — veja Comparações de Igualdade.

Note

C# não tem operador === . Escrever === constitui um erro de compilação:

// This does not compile — C# has no === operator
bool same = (x === 10);

Se vieres de JavaScript, usa == para comparação de valores (C# == já compara por valor para tipos primitivos e strings). Um bug relacionado comum é escrever acidentalmente = (atribuição) quando querias escrever == (comparação de igualdade). O compilador capta as formas mais comuns, mas verifica duas vezes qualquer if condição que contenha =.

Operadores condicional-lógicos

&& (E) e || (OU) combinam bool expressões.

int age = 20;
bool hasTicket = true;

// && (AND): both sides must be true
bool canEnter = age >= 18 && hasTicket;
Console.WriteLine(canEnter); // => True

// || (OR): at least one side must be true
bool freeEntry = age < 5 || age >= 65;
Console.WriteLine(freeEntry); // => False

// Short-circuit: right side is skipped when the result is already determined
// Here, items.Count is never called if items is null
List<string>? items = null;
bool hasItems = items != null && items.Count > 0;
Console.WriteLine(hasItems); // => False  (short-circuits; no NullReferenceException)

Ambos os operadores entram em curto-circuito: saltam a avaliação do operando direito quando o resultado já está determinado.

  • && retorna false assim que o lado esquerdo for false. O lado direito nunca é avaliado.
  • || retorna true assim que o lado esquerdo for true. O lado direito nunca é avaliado.

O comportamento de curto-circuito tem um benefício prático: pode proteger uma operação em segurança no lado direito com uma verificação nula no lado esquerdo, como mostra o exemplo acima. Se items for &&, o items.Count fica por aí — NullReferenceException nunca é chamado, por isso não é lançada nenhuma null.

Operador condicional ?:

O operador condicional (também chamado operador ternário ) avalia uma de duas expressões com base numa condição:

condition ? value-when-true : value-when-false
int temperature2 = 35;

// condition ? value-when-true : value-when-false
string weather = temperature2 > 30 ? "hot" : "comfortable";
Console.WriteLine(weather); // => hot

// Only the matching branch evaluates — the other branch is never run
int divisor = 0;
// The division 10 / divisor is never evaluated because divisor == 0 is true
int safe = divisor == 0 ? -1 : 10 / divisor;
Console.WriteLine(safe); // => -1

O ?: operador avalia sempre exatamente um ramo — o lado que não corresponde à condição nunca é avaliado. Isto permite utilizar em segurança, num dos lados, uma expressão que falharia para outros valores de entrada, desde que a condição a salvaguarde corretamente.

Use ?: para escolhas simples, em linha. Para condições com várias alternativas ou blocos de código, uma instrução if/else é geralmente mais clara.

Operadores de atribuição

O operador = de atribuição simples armazena um valor numa variável:

int level = 1;  // declaration + initialization
level = 5;      // reassignment

A atribuição em C# é associativa à direita, o que significa que a = b = c = 0 é avaliado da direita para a esquerda: c recebe 0, depois b recebe 0, e depois a recebe 0.

Atribuição composta

Operadores de atribuição composta combinam uma operação binária com a atribuição:

Operador Equivalente a
x += y x = x + y
x -= y x = x - y
x *= y x = x * y
x /= y x = x / y
x %= y x = x % y
int level = 1;
level = 5;          // simple assignment: replaces the value
Console.WriteLine(level); // => 5

// Compound assignment: short form of binary operation + assignment
int hp = 100;
hp += 20;   // same as: hp = hp + 20
Console.WriteLine(hp); // => 120
hp -= 10;   // same as: hp = hp - 10
Console.WriteLine(hp); // => 110
hp *= 2;    // same as: hp = hp * 2
Console.WriteLine(hp); // => 220
hp /= 3;    // same as: hp = hp / 3 (integer division)
Console.WriteLine(hp); // => 73
hp %= 7;    // same as: hp = hp % 7
Console.WriteLine(hp); // => 3

A atribuição composta é mais do que uma simples forma abreviada. Avalia o lado esquerdo exatamente uma vez e depois converte o resultado de volta para o tipo do lado esquerdo. Isto importa quando o lado esquerdo tem efeitos secundários (como um indexador de array), e é por isso que a atribuição composta a uma byte variável compila sem um cast explícito, enquanto a forma expandida não:

// Assignment is right-associative: evaluated right to left
int a2, b2, c2;
a2 = b2 = c2 = 0;     // c2 = 0 first, then b2 = 0, then a2 = 0
Console.WriteLine($"{a2} {b2} {c2}"); // => 0 0 0

// Compound assignment evaluates the left side once and converts back to the LHS type
byte small = 200;
small += 10;  // equivalent to: small = (byte)(small + 10); result is 210
Console.WriteLine(small); // => 210

small += 10 compila porque o compilador insere automaticamente a conversão de estreitamento — o resultado, 210, encaixa-se no byte intervalo de 0–255. small = small + 10 exigiria uma conversão explícita (byte), porque a aritmética promove ambos os operandos a int.

Outros operadores C#

Este artigo aborda os operadores que mais encontrará no código do dia a dia. A linguagem C# inclui mais operadores úteis em cenários específicos:

  • Operadores de deslocamento (<<, >>, >>>) — deslocam os bits de um valor inteiro para a esquerda ou direita por um número especificado de posições. Operadores bit a bit e operadores lógicos sobre números inteiros (&, |, ^, ~) — combinam ou invertem valores inteiros, um bit de cada vez, úteis em sinalizadores, máscaras e código de baixo nível: Operadores bit a bit e de deslocamento
  • checked e unchecked — controla se o overflow inteiro lança uma exceção (checked) ou se envolve silenciosamente (unchecked): Verificado e desverificado
  • Operadores nulos (??, ??=, ?., ?[]) — tratam null valores de forma segura fornecendo predefinidos ou curto-circuitando o acesso dos membros: Operadores nulos
  • Operadores de teste de tipo e conversão (is, as, typeof, cast (T)) — verificam ou convertem o tipo de execução de um valor: Operadores de teste de tipo e cast
  • Operadores de intervalo e índice (.., ^) — criam intervalos e índices relativos ao fim para dividir arrays e spans em fatias: Acesso a membros e operadores condicionais de nulo
  • Atribuição de desconstrução — descompactar uma tupla ou tipo em variáveis individuais numa única expressão: Desconstruir tuplas e outros tipos

Consulte também