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 ==.
- 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 += 2funciona numbyteevidas = vidas + 2nã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 que | No exemplo |
|---|---|---|
pontos += 5 | pontos = pontos + 5 | 10 → 15 |
pontos -= 3 | pontos = pontos - 3 | 15 → 12 |
pontos *= 2 | pontos = pontos * 2 | 12 → 24 |
pontos /= 5 | pontos = pontos / 5 | 24 → 4 |
pontos %= 3 | pontos = pontos % 3 | 4 → 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.
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
intde uma vez, ainda sem valor. - a = b = c = 7;
- Da direita para a esquerda:
c = 7guarda 7 e vale 7; entãob = 7; entãoa = 7. Útil para zerar várias variáveis. - int y = (x = 8) + 1;
- Primeiro
xrecebe 8; a expressão vale 8; mais 1, 9 vai paray. 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 que | Assunto |
|---|---|---|
x = y | guarde y em x | esta aula |
x += y, -=, *=, /=, %= | x = (tipo de x)(x + y)… | esta aula |
x ??= y | se x é null, x = y | Operadores 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
| Escrita | Lê-se | Exemplo (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 = 0 | em 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;:
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 comint) e%=. texto += "…"acrescenta a uma string.- A atribuição vale o valor guardado:
a = b = c = 0. - O
+=tem um cast embutido: funciona embyte(e pode dar a volta: 250 + 10 = 4), mas não aceitadoublenumint(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).