Operatori di Assegnazione in C#

Gli operatori di assegnazione in C# sono strumenti fondamentali che consentono di assegnare valori alle variabili e, nel caso degli operatori composti, di eseguire operazioni aritmetiche o logiche insieme all’assegnazione. Questi operatori rendono il codice più conciso e leggibile, fornendo un modo elegante per modificare i valori delle variabili attraverso sintassi compatta ed espressiva.
Operatore di Assegnazione Base
Operatore = (Assegnazione Semplice)
L’operatore = è l’operatore di assegnazione fondamentale che assegna il valore dell’espressione di destra alla variabile di sinistra.
int numero = 42;
string nome = "Mario";
bool attivo = true;
double prezzo = 19.99;
// Assegnazione multipla (stesso valore)
int a, b, c;
a = b = c = 10; // Tutte le variabili ricevono il valore 10
Tabella Completa degli Operatori di Assegnazione
| Operatore | Nome | Esempio | Equivalente | Descrizione |
|---|---|---|---|---|
= |
Assegnazione | x = 5 |
- | Assegna valore alla variabile |
+= |
Addizione e assegnazione | x += 3 |
x = x + 3 |
Somma e assegna |
-= |
Sottrazione e assegnazione | x -= 3 |
x = x - 3 |
Sottrae e assegna |
*= |
Moltiplicazione e assegnazione | x *= 3 |
x = x * 3 |
Moltiplica e assegna |
/= |
Divisione e assegnazione | x /= 3 |
x = x / 3 |
Divide e assegna |
%= |
Modulo e assegnazione | x %= 3 |
x = x % 3 |
Calcola resto e assegna |
&= |
AND bitwise e assegnazione | x &= 3 |
x = x & 3 |
AND bitwise e assegna |
|= |
OR bitwise e assegnazione | x |= 3 |
x = x | 3 |
OR bitwise e assegna |
^= |
XOR bitwise e assegnazione | x ^= 3 |
x = x ^ 3 |
XOR bitwise e assegna |
<<= |
Shift sinistro e assegnazione | x <<= 2 |
x = x << 2 |
Shift a sinistra e assegna |
>>= |
Shift destro e assegnazione | x >>= 2 |
x = x >> 2 |
Shift a destra e assegna |
??= |
Null-coalescing e assegnazione | x ??= 5 |
x = x ?? 5 |
Assegna se null (C# 8.0+) |
Operatori Aritmetici Composti
Addizione e Assegnazione (+=)
int contatore = 10;
contatore += 5; // contatore diventa 15
// Con stringhe (concatenazione)
string messaggio = "Ciao";
messaggio += " Mondo"; // messaggio diventa "Ciao Mondo"
// Con collezioni
List<int> numeri = new List<int> {1, 2, 3};
// numeri += 4; // Errore! Non supportato direttamente
// Usa numeri.Add(4) invece
Sottrazione e Assegnazione (-=)
double saldo = 1000.50;
saldo -= 250.00; // saldo diventa 750.50
int vite = 3;
vite -= 1; // vite diventa 2
Moltiplicazione e Assegnazione (*=)
int area = 5;
area *= 4; // area diventa 20
double prezzo = 19.99;
prezzo *= 1.22; // Applica IVA del 22%
Divisione e Assegnazione (/=)
double totale = 100.0;
totale /= 4; // totale diventa 25.0
int punteggio = 85;
punteggio /= 5; // punteggio diventa 17 (divisione intera)
Modulo e Assegnazione (%=)
int numero = 17;
numero %= 5; // numero diventa 2 (resto di 17/5)
// Utile per operazioni cicliche
int indice = 10;
int dimensioneArray = 7;
indice %= dimensioneArray; // indice diventa 3
Operatori Bitwise Composti
AND Bitwise e Assegnazione (&=)
int flags = 0b1111; // 15 in binario
flags &= 0b1010; // flags diventa 0b1010 (10)
// Esempio con enum flags
[Flags]
enum Permessi
{
Nessuno = 0,
Lettura = 1,
Scrittura = 2,
Esecuzione = 4
}
Permessi userPermessi = Permessi.Lettura | Permessi.Scrittura | Permessi.Esecuzione;
userPermessi &= ~Permessi.Esecuzione; // Rimuove permesso di esecuzione
OR Bitwise e Assegnazione (|=)
int flags = 0b1010; // 10 in binario
flags |= 0b0101; // flags diventa 0b1111 (15)
// Aggiunta di permessi
Permessi userPermessi = Permessi.Lettura;
userPermessi |= Permessi.Scrittura; // Aggiunge permesso di scrittura
XOR Bitwise e Assegnazione (^=)
int numero = 0b1010; // 10
numero ^= 0b1100; // numero diventa 0b0110 (6)
// Toggle di bit specifici
bool stato = true;
stato ^= true; // stato diventa false (toggle)
Operatori di Shift Composti
Shift Sinistro e Assegnazione (<<=)
int valore = 5; // 0b0101
valore <<= 2; // valore diventa 20 (0b10100)
// Equivale a moltiplicare per 2^n
int numero = 3;
numero <<= 3; // numero diventa 24 (3 * 2^3)
Shift Destro e Assegnazione (>>=)
int valore = 20; // 0b10100
valore >>= 2; // valore diventa 5 (0b0101)
// Equivale a dividere per 2^n
int numero = 32;
numero >>= 3; // numero diventa 4 (32 / 2^3)
Operatore Null-Coalescing e Assegnazione (??=)
Introdotto in C# 8.0, questo operatore assegna un valore solo se la variabile è null.
string nome = null;
nome ??= "Valore predefinito"; // nome diventa "Valore predefinito"
string cognome = "Esistente";
cognome ??= "Nuovo valore"; // cognome rimane "Esistente"
// Utile per inizializzazione lazy
private List<string> _elementi;
public List<string> Elementi
{
get
{
_elementi ??= new List<string>();
return _elementi;
}
}
// Con proprietà nullable
public class Utente
{
public string? Email { get; set; }
public void ImpostaEmailPredefinita()
{
Email ??= "noreply@example.com";
}
}
Esempi Pratici
Contatori e Accumulatori
public class ContatorePunteggio
{
private int punteggio = 0;
private int moltiplicatore = 1;
public void AggiungiPunti(int punti)
{
punteggio += punti * moltiplicatore;
}
public void ApplicaBonus()
{
moltiplicatore *= 2;
}
public void ApplicaPenalita()
{
punteggio /= 2;
}
public void ResetMoltiplicatore()
{
moltiplicatore = 1;
}
}
Gestione di Configurazioni
public class ConfigurazioneApp
{
public string? DatabaseUrl { get; set; }
public int? Timeout { get; set; }
public bool? DebugMode { get; set; }
public void ApplicaConfigurazioniPredefinite()
{
DatabaseUrl ??= "localhost:5432";
Timeout ??= 30;
DebugMode ??= false;
}
public void AggiornaTimeout(int incremento)
{
Timeout = Timeout ?? 30;
Timeout += incremento;
}
}
var config = new ConfigurazioneApp();
config.ApplicaConfigurazioniPredefinite();
config.AggiornaTimeout(10); // Timeout diventa 40
Manipolazione di Collezioni
public class GestoreStatistiche
{
private Dictionary<string, int> statistiche = new();
public void IncrementaStatistica(string chiave)
{
if (statistiche.ContainsKey(chiave))
statistiche[chiave] += 1;
else
statistiche[chiave] = 1;
}
public void AggiungiPunti(string chiave, int punti)
{
statistiche.TryGetValue(chiave, out int valoreAttuale);
statistiche[chiave] = valoreAttuale + punti;
}
public void ApplicaMoltiplicatore(string chiave, double moltiplicatore)
{
if (statistiche.ContainsKey(chiave))
{
statistiche[chiave] = (int)(statistiche[chiave] * moltiplicatore);
}
}
}
Operatori di Assegnazione con Tipi Personalizzati
Overload degli Operatori
public struct Vettore
{
public double X { get; set; }
public double Y { get; set; }
public Vettore(double x, double y)
{
X = x;
Y = y;
}
public static Vettore operator +(Vettore a, Vettore b)
{
return new Vettore(a.X + b.X, a.Y + b.Y);
}
public static Vettore operator *(Vettore v, double scalare)
{
return new Vettore(v.X * scalare, v.Y * scalare);
}
}
// Utilizzo con operatori composti
Vettore velocità = new Vettore(10, 5);
Vettore accelerazione = new Vettore(2, 1);
velocità += accelerazione; // Usa l'overload di +
velocità *= 1.5; // Usa l'overload di *
Proprietà con Operatori Composti
public class BankAccount
{
private decimal _saldo;
public decimal Saldo
{
get => _saldo;
private set => _saldo = value;
}
public void Deposita(decimal importo)
{
Saldo += importo;
}
public bool Preleva(decimal importo)
{
if (Saldo >= importo)
{
Saldo -= importo;
return true;
}
return false;
}
public void ApplicaInteressi(decimal tasso)
{
Saldo *= (1 + tasso);
}
}
Best Practices e Considerazioni
Performance e Leggibilità
// ✅ Operatori composti per chiarezza
contatore += 1;
saldo -= commissione;
prezzo *= fattoreSconto;
// ❌ Equivalente ma meno leggibile
contatore = contatore + 1;
saldo = saldo - commissione;
prezzo = prezzo * fattoreSconto;
// ✅ Per incrementi semplici, considera ++ e --
contatore++; // Invece di contatore += 1
indice--; // Invece di indice -= 1
Attenzione ai Tipi
// Attenzione alla conversione di tipi
int intero = 10;
double decimale = 3.7;
// intero += decimale; // Errore di compilazione
intero += (int)decimale; // Cast esplicito necessario
// Con tipi nullable
int? nullable = null;
// nullable += 5; // Errore! nullable è null
nullable = (nullable ?? 0) + 5; // Gestione sicura
Operatori Bitwise - Casi d’Uso
// Gestione di flag
[Flags]
public enum OpzioniFile
{
Nessuna = 0,
Lettura = 1,
Scrittura = 2,
Nascosto = 4,
Sistema = 8
}
OpzioniFile opzioni = OpzioniFile.Lettura;
opzioni |= OpzioniFile.Scrittura; // Aggiunge scrittura
opzioni &= ~OpzioniFile.Nascosto; // Rimuove nascosto (se presente)
// Operazioni su bit per ottimizzazione
public class BitSet
{
private uint bits = 0;
public void Set(int posizione)
{
bits |= (uint)(1 << posizione);
}
public void Clear(int posizione)
{
bits &= ~(uint)(1 << posizione);
}
public void Toggle(int posizione)
{
bits ^= (uint)(1 << posizione);
}
}
Errori Comuni da Evitare
Confusione tra = e ==
int x = 5;
// ❌ Errore comune - assegnazione invece di confronto
if (x = 10) // Errore di compilazione in C#
{
// Questo codice non compilerà
}
// ✅ Confronto corretto
if (x == 10)
{
// Confronto di uguaglianza
}
Overflow e Underflow
byte valore = 255;
valore += 1; // Overflow! valore diventa 0
// ✅ Controllo sicuro
checked
{
try
{
byte sicuro = 255;
sicuro += 1; // Genererà OverflowException
}
catch (OverflowException)
{
Console.WriteLine("Overflow rilevato!");
}
}
Conclusione
Gli operatori di assegnazione in C# sono strumenti essenziali che rendono il codice più conciso e leggibile. La tabella completa degli operatori mostra la ricchezza di opzioni disponibili, dai semplici operatori aritmetici a quelli bitwise avanzati, fino al moderno operatore null-coalescing. Utilizzare correttamente questi operatori migliora significativamente la qualità del codice, rendendolo più espressivo e manutenibile. È importante comprendere le implicazioni di ogni operatore, especialmente in termini di tipi, performance e comportamento con valori null, per sfruttare al meglio la potenza e la flessibilità di C#.