Menu

Operatori in C#: aritmetici, modulo, logici, bitwise e precedenza

Gli operatori di C# con il loro comportamento esatto: divisione intera, l'operatore modulo con i numeri negativi, incremento e assegnazione composta, confronto, logica con cortocircuito, operatori bitwise, gli operatori per null e una tabella delle precedenze.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Un operatore prende uno, due o tre operandi e produce un valore: a + b, !done, x ?? fallback. Gli operatori di C# sono simili a quelli di C, Java e JavaScript, ma alcuni si comportano in modi che sorprendono chi inizia, soprattutto la divisione intera e il segno di %.

Operatori aritmetici

+, -, *, / e % funzionano su tutti i tipi numerici. Il tipo del risultato dipende dagli operandi: due valori int producono un int, un int e un double producono un double.

Output:

22
12
85
3
2
3.4
3.4
3
3.4

La divisione intera tronca verso lo zero. 17 / 5 vale 3 e -17 / 5 vale -3. È la causa di un bug molto comune: int percent = done / total * 100; vale 0 ogni volta che done < total, perché done / total è già 0. Moltiplica prima (done * 100 / total) oppure dividi come double.

Dividere un intero o un decimal per zero lancia DivideByZeroException. Dividere un double per zero non lancia nulla; produce infinito, oppure NaN per 0.0 / 0.0:

Output:

True
True
False
DivideByZeroException

L'operatore modulo e i numeri negativi

% restituisce il resto della divisione, e il suo risultato prende il segno dell'operando di sinistra. Questo è diverso da Python e dal "mod" matematico, che sono sempre non negativi con un divisore positivo:

Output:

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

Usa la funzione di supporto Mod ogni volta che il lato sinistro può essere negativo e il risultato è un indice: days[(today + offset) % 7] lancerebbe una IndexOutOfRangeException con una somma negativa. Anche il test per i numeri dispari ha la stessa trappola: n % 2 == 1 è false per i numeri dispari negativi; verifica n % 2 != 0.

Assegnazione, assegnazione composta e incremento

= assegna. Le forme composte +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>= combinano un'operazione con l'assegnazione. ++ e -- aggiungono o sottraggono 1, ed esistono in forma prefissa e postfissa, che differiscono nel valore che restituiscono:

Output:

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

Il caso del byte mostra una particolarità: level = level + 10; non compila, perché byte + int è un int, mentre level += 10; sì, perché l'assegnazione composta include un cast implicito verso il tipo della variabile. Quel cast ricomincia da capo in caso di overflow (250 + 10 diventa 4).

Operatori di confronto e logici

Gli operatori di confronto (==, !=, <, >, <=, >=) restituiscono un bool. Per le stringhe, == confronta il testo, non i riferimenti. Gli operatori logici sono ! (not), && (and), || (or) e ^ (or esclusivo).

Output:

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

&& e || cortocircuitano: l'operando di destra viene eseguito solo quando può cambiare il risultato. È questo che rende sicuro user != null && user.IsActive. I singoli & e | sui booleani valutano sempre entrambi i lati, cosa che raramente vuoi in una condizione.

Operatori bitwise e di shift

Sui tipi interi, &, |, ^ e ~ lavorano sui singoli bit, e << e >> li spostano. Si usano per flag, maschere, hashing e protocolli di basso livello:

Output:

3
True
False
101
1024
125
5
-1

Per insiemi di flag con un nome, un enum con l'attributo [Flags] è più chiaro delle costanti intere, e HasFlag si legge meglio di una maschera. C# 11 ha aggiunto >>>, uno shift a destra senza segno che riempie con zeri anche per i numeri negativi.

Operatori per null

Esistono tre operatori per lavorare con valori che possono essere null:

Output:

Guest

0
5

a ?? b restituisce a a meno che non sia null, altrimenti b. a?.Member restituisce null invece di lanciare un'eccezione quando a è null. C# 8 ha aggiunto a ??= b, che assegna b ad a solo quando a è null. Sono trattati a fondo nei tipi nullable.

Precedenza degli operatori

Quando un'espressione mescola operatori, le righe più in alto legano più strettamente. Gli operatori della stessa riga si raggruppano da sinistra a destra, tranne l'assegnazione, ?? e ?:, che si raggruppano da destra a sinistra. (Gli operandi vengono sempre valutati da sinistra a destra.)

CategoriaOperatori
Primarix.y, x?.y, f(x), a[i], x++, x--, new, typeof, nameof
Unari+x, -x, !x, ~x, ++x, --x, (T)x, await
Moltiplicativi*, /, %
Additivi+, -
Shift<<, >>, >>>
Relazionali e test di tipo<, >, <=, >=, is, as
Uguaglianza==, !=
AND bit a bit&
XOR bit a bit^
OR bit a bit|
AND condizionale&&
OR condizionale||
Null coalescing??
Condizionalec ? a : b
Assegnazione e lambda=, +=, -=, ??=, => e le altre forme composte

Due conseguenze da conoscere: a + b * c moltiplica per prima cosa, e x & mask == 0 confronta per primo (== lega più strettamente di &), quindi significa x & (mask == 0) e non compila con gli interi. Scrivi (x & mask) == 0. Le parentesi non costano nulla e rendono evidente l'intenzione.

Overloading degli operatori

I tuoi tipi possono definire cosa significano gli operatori. È così che decimal, DateTime e TimeSpan supportano + e <:

Output:

33.49
True

Gli operatori devono essere public static, e alcuni vanno obbligatoriamente in coppia: definire < richiede >, e == richiede !=. Con == definito, il compilatore avvisa anche finché non ridefinisci Equals e GetHashCode in modo coerente. Fai l'overloading degli operatori solo dove il significato è ovvio, come con numeri, vettori e importi di denaro.

Domande frequenti

Cosa fa % in C#?

% è l'operatore di resto: 17 % 5 vale 2. Il risultato ha il segno dell'operando di sinistra, quindi -7 % 3 vale -1, non 2 come in Python. Per un risultato sempre non negativo, come serve quando fai ricominciare un indice da capo, scrivi ((a % n) + n) % n. Funziona anche su double e decimal: 7.5 % 2 vale 1.5.

Perché 7 / 2 fa 3 in C#?

Quando entrambi gli operandi sono interi, / esegue la divisione intera e scarta la parte frazionaria, troncando verso lo zero. Rendi uno degli operandi un valore in virgola mobile o decimal per ottenere un risultato con la parte decimale: 7 / 2.0 vale 3.5, (double)a / b converte prima di dividere, e 7m / 2 vale 3.5 come decimal.

Che differenza c'è tra && e & in C#?

&& è l'AND condizionale: se il lato sinistro è false, il lato destro non viene valutato. & sui booleani valuta sempre entrambi i lati, e sugli interi è l'AND bit a bit. Usa && e || per le condizioni, soprattutto per controlli come user != null && user.IsActive, che con & lancerebbe una NullReferenceException.

Che differenza c'è tra i++ e ++i in C#?

Entrambi aggiungono 1 a i. L'espressione i++ vale il vecchio valore, ++i il nuovo. Su una riga da sola (i++;) non c'è differenza. Conta solo quando il risultato viene usato, come in int a = i++;, ed è per questo che molte guide di stile tengono gli incrementi su una riga a parte.

Qual è la precedenza degli operatori in C#?

Dalla più alta alla più bassa: primari (x.y, f(x), a[i], x++), unari (!, -, ++x, cast), moltiplicativi (* / %), additivi (+ -), shift, relazionali (< > <= >= is as), uguaglianza (== !=), &, ^, |, &&, ||, ??, il condizionale ?: e infine l'assegnazione. Nel dubbio, aggiungi le parentesi.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA