C# Operadores de Atribuição

Você usa o = desde a primeira variável: int pontos = 10;. Ele é um operador de atribuição: guarda o valor da direita na variável da esquerda. Mas programas mudam valores o tempo todo ("soma 5 pontos", "tira 1 vida", "dobra o saldo"), e para isso o C# tem atalhos: +=, -=, *=, /= e %=. Nesta aula você vai ver cada um, o que acontece por trás e os erros mais comuns, como confundir = com ==.

Você vai aprender
  • O que o = faz de verdade: "guarde", e não "é igual".
  • As formas curtas: +=, -=, *=, /= e %=.
  • += com texto, para ir montando uma string.
  • A atribuição em cadeia (a = b = c = 0).
  • A conversão escondida: por que vidas += 2 funciona num byte e vidas = vidas + 2 não.
  • Os outros: ??= e os de bits (<<=, |=…).
  • Os erros: = no lugar de ==, =+ no lugar de +=, variável sem valor e constante.

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


A ideia: o placar do jogo

Num placar de jogo, ninguém escreve o número do zero a cada ponto. O juiz olha o número que está lá, soma 1 e põe o novo no lugar. "Pegue o valor atual, faça uma conta e guarde o resultado de volta" é a coisa que um programa mais faz, e é para isso que existem os operadores de atribuição.

Antes, um detalhe importante: no C#, = não quer dizer "é igual", como na matemática. Quer dizer "guarde". Leia pontos = 10 como "pontos recebe 10". Para perguntar se dois valores são iguais existe outro operador, o == (aula Operadores de Comparação).


1. O =: guardar um valor

O = funciona sempre da direita para a esquerda: primeiro o C# calcula tudo o que está à direita, depois guarda o resultado na variável à esquerda. Por isso esta linha, que seria absurda na matemática, é a mais comum da programação:

pontos = pontos + 5;

"Pegue o valor atual de pontos, some 5, e guarde o resultado em pontos." Se pontos valia 10, agora vale 15. O valor antigo é substituído.


2. As formas curtas: +=, -=, *=, /=, %=

Como pontos = pontos + 5 aparece o tempo todo, o C# tem um atalho: pontos += 5. O nome da variável aparece uma vez só:

int pontos = 10;
pontos += 5;
Console.WriteLine(pontos);
pontos -= 3;
Console.WriteLine(pontos);
pontos *= 2;
Console.WriteLine(pontos);
pontos /= 5;
Console.WriteLine(pontos);
pontos %= 3;
Console.WriteLine(pontos);
$ dotnet run atrib1.cs
15
12
24
4          « 24 / 5 = 4 (divisão inteira)
1          « 4 % 3 = 1
Forma curtaÉ o mesmo queNo exemplo
pontos += 5pontos = pontos + 510 → 15
pontos -= 3pontos = pontos - 315 → 12
pontos *= 2pontos = pontos * 212 → 24
pontos /= 5pontos = pontos / 524 → 4
pontos %= 3pontos = pontos % 34 → 1

As contas seguem as regras da aula Operadores Aritméticos: com int, o /= faz divisão inteira (24 / 5 = 4, não 4,8). E o lado direito pode ser qualquer conta: total += preco * quantidade; calcula preco * quantidade primeiro e soma o resultado ao total.

E o pontos += 1?

Somar ou subtrair 1 é tão comum que tem um atalho ainda menor: pontos++ e pontos--. Eles têm aula própria: ++ e --.

O cofrinho da Ana

decimal cofrinho = 0m;
cofrinho += 10m;
cofrinho += 5.50m;
cofrinho -= 3m;
cofrinho *= 2;
Console.WriteLine($"Cofrinho: R$ {cofrinho}");
$ dotnet run cofrinho.cs
Cofrinho: R$ 25,00

0 + 10 + 5,50 - 3 = 12,50; dobrado, 25,00. Com decimal, o *= 2 aceita o int 2 sem problema, e as casas decimais se mantêm.

Missão: o jogo começa com 100 moedas. Troque cada comentário por uma linha com a forma curta certa: o jogador ganha 50 (+=), gasta 30 (-=) e depois tem o saldo dobrado (*=). (100 + 50 - 30) × 2 = Saldo: 240.

int moedas = 100;
// ganha 50
// gasta 30
// dobra
Console.WriteLine($"Saldo: {moedas}");
int moedas = 100;
moedas += 50;
moedas -= 30;
moedas *= 2;
Console.WriteLine($"Saldo: {moedas}");

3. += com texto

Como o + junta textos, o += serve para ir acrescentando a uma string:

string lista = "arroz";
lista += ", feijão";
lista += ", café";
Console.WriteLine(lista);
string texto = "Pontos: ";
texto += 10;
Console.WriteLine(texto);
$ dotnet run atribstring.cs
arroz, feijão, café
Pontos: 10

Até um número pode ser acrescentado: ele vira texto ("Pontos: " + 10). Para juntar muitos pedaços, num laço com milhares de voltas, existe o StringBuilder, que é bem mais rápido. Os outros atalhos (-=, *=…) não funcionam com texto.


4. A atribuição também é um valor

No C#, x = 8 não só guarda o 8: a expressão inteira também vale 8. Isso permite a atribuição em cadeia:

int a, b, c;
a = b = c = 7;
Console.WriteLine($"{a} {b} {c}");
int x = 5;
int y = (x = 8) + 1;
Console.WriteLine($"{x} {y}");
$ dotnet run atrib2.cs
7 7 7
8 9
int a, b, c;
Cria três variáveis int de uma vez, ainda sem valor.
a = b = c = 7;
Da direita para a esquerda: c = 7 guarda 7 e vale 7; então b = 7; então a = 7. Útil para zerar várias variáveis.
int y = (x = 8) + 1;
Primeiro x recebe 8; a expressão vale 8; mais 1, 9 vai para y. Funciona, mas é difícil de ler: evite. Uma coisa por linha é sempre mais claro.

5. A conversão escondida do +=

Na aula Inteiros você viu que uma conta com byte é feita em int, e que o resultado não volta sozinho para o byte. Mas veja:

byte vidas = 3;
vidas += 2;
Console.WriteLine(vidas);
$ dotnet run atribbyte.cs
5
byte vidas = 3;
vidas = vidas + 2;
Console.WriteLine(vidas);
$ dotnet run atribbyteerro.cs
/home/ana/cs/atribbyteerro.cs(2,9): error CS0266: Não é possível converter implicitamente tipo "int" em "byte". Existe uma conversão explícita (há uma conversão ausente?)

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

Então vidas += 2 não é exatamente vidas = vidas + 2. A forma curta tem um cast embutido: é vidas = (byte)(vidas + 2). O C# deixa porque você está guardando de volta na mesma variável, então o tipo certo é óbvio. O lado ruim: o cast escondido pode derramar sem aviso, como qualquer cast (aula Conversão de Tipos):

byte b = 250;
b += 10;
Console.WriteLine(b);
$ dotnet run atribbyte2.cs
4          « 260 não cabe num byte: deu a volta

O cast escondido só existe quando o valor da direita já "caberia" no tipo da variável. Somar um double a um int continua proibido:

int total = 10;
total += 2.5;
Console.WriteLine(total);
$ dotnet run atribdouble.cs
/home/ana/cs/atribdouble.cs(2,1): error CS0266: Não é possível converter implicitamente tipo "double" em "int". Existe uma conversão explícita (há uma conversão ausente?)

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

E o checked também vale para as formas curtas: dentro de checked { … }, um saldo += 1 que estoura lança OverflowException.


6. Os outros operadores de atribuição

Todo operador de duas partes tem a sua forma com =. Dois grupos aparecem mais adiante no guia:

string? apelido = null;
apelido ??= "Sem apelido";
Console.WriteLine(apelido);
apelido ??= "Outro";
Console.WriteLine(apelido);
$ dotnet run atribnull.cs
Sem apelido
Sem apelido

O ??= C# 8 guarda o valor só se a variável estiver null: na primeira vez guardou; na segunda, apelido já tinha valor e nada mudou. Ele é explicado em Operadores ??, ?. e ??=.

int n = 1;
n <<= 3;
Console.WriteLine(n);
n >>= 1;
Console.WriteLine(n);
n |= 1;
Console.WriteLine(n);
n &= 6;
Console.WriteLine(n);
n ^= 7;
Console.WriteLine(n);
$ dotnet run atribbits.cs
8
4
5
4
3

Esses mexem nos bits do número (deslocar, "ou", "e", "ou exclusivo"). São raros no dia a dia; o &= e o |= com bool aparecem em Operadores Lógicos.

OperadorÉ o mesmo queAssunto
x = yguarde y em xesta aula
x += y, -=, *=, /=, %=x = (tipo de x)(x + y)…esta aula
x ??= yse x é null, x = yOperadores null
x &= y, |=, ^=x = x & y…bits e bool
x <<= n, >>=, >>>=x = x << n…deslocamento de bits (>>>= é do C# 11)

Quando dá errado

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

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

O que significa: a intenção era perguntar "x é igual a 5?", mas x = 5 guarda 5 e vale 5, um int. O if precisa de um bool. Em C, esse erro passa despercebido; no C#, não compila. O aviso CS0219 vem junto porque, para o compilador, x só recebe valores e nunca é lido.

Como consertar: para comparar, ==: if (x == 5).

=+ no lugar de +=: compila, mas o valor sai errado
int x = 10;
x =+ 5;
Console.WriteLine(x);
x =- 3;
Console.WriteLine(x);
$ dotnet run atribespaco.cs
5          « esperava 15
-3         « esperava 2

O que significa: x =+ 5 é lido como x = +5 ("guarde mais cinco"), e x =- 3 como x = -3. Nenhum erro, nenhum aviso.

Como consertar: o sinal da conta vem antes do igual: +=, -=. Um truque para lembrar: "mais, igual", na ordem em que se fala.

error CS0165: Uso de variável local não atribuída "total"
int total;
total += 5;
Console.WriteLine(total);
$ dotnet run atribnaoatrib.cs
/home/ana/cs/atribnaoatrib.cs(2,1): error CS0165: Uso de variável local não atribuída "total"

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

O que significa: total += 5 precisa ler o valor atual de total, e ele não tem valor nenhum.

Como consertar: comece do zero: int total = 0;.

error CS0131: O lado esquerdo de uma atribuição deve ser uma variável, uma propriedade ou um indexador

O que significa: à esquerda do = (ou do +=) há algo que não pode receber valor: uma constante (Limite += 1), um número (5 = x) ou uma conta (a + b = 10).

Como consertar: a variável que recebe fica sempre à esquerda, sozinha: x = 5, c = a + b.

error CS0266 (ou CS0019) num +=

O que significa: o valor da direita é de um tipo que não entra na variável: total += 2.5 com total int dá CS0266; saldo += 0.1 com saldo decimal dá CS0019 (decimal e double não se misturam).

Como consertar: use o tipo certo do lado direito (0.1m) ou mude o tipo da variável (double total).


Referência rápida

EscritaLê-seExemplo (x vale 10)
x = 5"x recebe 5"5
x += 5"some 5 a x"15
x -= 5"tire 5 de x"5
x *= 5"multiplique x por 5"50
x /= 5"divida x por 5"2
x %= 3"x recebe o resto de x por 3"1
texto += "abc""acrescente abc"—
a = b = 0em cadeia, da direita para a esquerda—
x ??= y"se x é null, recebe y"—

Exercícios

1. A forma curta

Complete com a forma curta de saldo = saldo - 20;:

saldo 20;

2. Quanto vale?

int n = 7; n *= 3; n -= 1; Quanto vale n?

3. Divisão curta

int n = 17; n /= 5; Quanto vale n?

4. int x = 10; x =+ 3; Quanto vale x?

5. byte b = 3; b += 2; compila, mas b = b + 2; não. Por quê?

6. O que if (idade = 18) causa?

Missão final: a lista de compras

Missão: a Ana vai montando a lista e o total das compras. As duas primeiras compras já estão feitas. Complete a terceira (ovos, R$ 13,90) com lista += … e total += …, para mostrar Compras: pão, leite, ovos e Total: 23,40.

string lista = "pão";
decimal total = 4.50m;

lista += ", leite";
total += 5.00m;

// terceira compra: ovos, 13.90m

Console.WriteLine($"Compras: {lista}");
Console.WriteLine($"Total: {total}");
string lista = "pão";
decimal total = 4.50m;

lista += ", leite";
total += 5.00m;

lista += ", ovos";
total += 13.90m;

Console.WriteLine($"Compras: {lista}");
Console.WriteLine($"Total: {total}");

Resumo

  • = quer dizer "guarde": calcula a direita e guarda na esquerda. pontos = pontos + 5 é normal. Para comparar, ==.
  • Formas curtas: x += y é x = x + y; o mesmo com -=, *=, /= (divisão inteira com int) e %=.
  • texto += "…" acrescenta a uma string.
  • A atribuição vale o valor guardado: a = b = c = 0.
  • O += tem um cast embutido: funciona em byte (e pode dar a volta: 250 + 10 = 4), mas não aceita double num int (CS0266).
  • ??= guarda só se a variável é null; &=, |=, ^=, <<=, >>= são para bits.
  • Cuidado com =+ (é = +, sem erro) e com variável sem valor antes do += (CS0165).