Operatori C#

Tip

Questo articolo fa parte della sezione Nozioni fondamentali per gli sviluppatori che già conoscono almeno un linguaggio di programmazione e stanno imparando C#. Se non si ha familiarità con la programmazione, iniziare prima con le esercitazioni introduttive .

Venire da un'altra lingua? La maggior parte degli operatori in questo articolo (+, -, /*, %, !==&&!=<||>operatori di confronto e =) funziona come in Java, C++e JavaScript. Le principali sorprese per i nuovi utenti sono il comportamento della divisione intera, la distinzione tra prefisso e postfisso per ++/--, e il modo in cui l'assegnazione composta riconverte al tipo del lato sinistro.

Un operatore combina uno o più operandi in un singolo valore. Si conoscono già le espressioni e la precedenza dell'operatore dalle espressioni C#; questo articolo illustra in modo più approfondito gli operatori specifici che verranno usati ogni giorno.

Operatori aritmetici

I cinque operatori aritmetici eseguono calcoli numerici.

Operatore Name Example Risultato
+ Aggiunta 10 + 3 13
- Sottrazione 10 - 3 7
* Moltiplicazione 10 * 3 30
/ Divisione 10 / 3 3
% Residuo 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)

La divisione intera trunca verso zero. Quando entrambi gli operandi sono numeri interi, / rimuove la parte frazionaria: 7 / 2 è 3, non 3.5. Il troncamento è verso zero, non verso il numero minore: -7 / 2 è -3 (non -4). Per ottenere un risultato decimale, creare almeno un operando di tipo a virgola mobile: 7.0 / 2 è 3.5. Ciò differisce da alcune lingue in cui / produce sempre un risultato a virgola mobile.

Il resto (%) restituisce ciò che resta dopo la divisione intera: 10 % 3 è 1 perché 10 = 3 × 3 + 1. È utile per scorrere un intervallo fisso (index % length), testare la divisione (n % 2 == 0) ed estrarre cifre. Con operandi negativi, il segno del risultato corrisponde al segno del dividendo (l'operando sinistro): -7 % 3 è -1 e 7 % -3 è 1.

Operatori unari

Gli operatori unari agiscono su un singolo 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 (più unario) — lascia invariato il valore; raramente viene scritto esplicitamente, ma è valido.
  • -x (meno unario): nega il valore.
  • !x (NOT logico): si capovolge true a false e false a true. Si userà ! spesso: if (!list.Contains(item)).

Incremento e decremento

++ aggiunge 1 e -- sottrae 1. Entrambi hanno un formato di prefisso e un formato di prefisso che differiscono in quale valore viene restituito:

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
  • Prefisso (++i, --i): incrementa o decrementa prima la variabile, quindi restituisce il nuovo valore.
  • Postfix (i++, i--): restituisce prima il valore corrente , quindi incrementa o decrementa la variabile.

Quando ++ o -- viene visualizzato come istruzione autonoma (non parte di un'espressione più grande), il prefisso e il suffisso hanno lo stesso effetto. La distinzione è importante solo quando viene usato il risultato, ad esempio in un'assegnazione o come argomento del metodo.

Operatori relazionali

Gli operatori relazionali confrontano due valori e restituiscono un oggetto bool.

Operatore Meaning Example
< Minore di speed < limit
> Maggiore di speed > limit
<= Minore o uguale a score <= 100
>= Maggiore di o uguale a 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)

Gli operatori relazionali funzionano su tutti i tipi numerici e char. Per char, il confronto usa il valore del punto di codice Unicode numerico del carattere, non qualsiasi ordinamento alfabetico o specifico del dominio. Nell'esempio di grado precedente, 'B' è maggiore o uguale a 'A' perché 'B' ha valore Unicode 66 e 'A' ha valore Unicode 65. I numeri determinano il confronto, non il significato dei voti delle lettere.

Operatori di uguaglianza

== e != controllare se due valori sono uguali o meno. != è true quando gli operandi non sono uguali, e false quando sono.

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

Per i tipi numerici e per string, i test di uguaglianza confrontano i valori. Per i tipi di riferimento, l'impostazione predefinita è Identity (indipendentemente dal fatto che due variabili puntino allo stesso oggetto), ma molti tipi, inclusi string ed record eseguono l'override di questo tipo per confrontare il contenuto. Per un quadro completo, ovvero il funzionamento dell'uguaglianza tra tipi di valore, tipi di riferimento, record e struct, vedere Confronti di uguaglianza.

Note

C# non ha un === operatore. La scrittura === è un errore in fase di compilazione:

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

Se si proviene da JavaScript, usare == per confrontare i valori (C# == confronta già per valore i tipi primitivi e le stringhe). Un errore correlato comune consiste nello scrivere accidentalmente = (assegnazione) quando invece si voleva usare == (confronto di uguaglianza). Il compilatore rileva i moduli più comuni, ma controlla due volte qualsiasi if condizione che contiene =.

Operatori logici condizionali

&& (AND) e || (OR) combinano bool espressioni.

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)

Entrambi gli operatori hanno un corto circuito: ignorano la valutazione dell'operando destro quando il risultato è già determinato.

  • && restituisce false non appena il lato sinistro è false. Il lato destro non viene mai valutato.
  • || restituisce true non appena il lato sinistro è true. Il lato destro non viene mai valutato.

Il comportamento a corto circuito offre un vantaggio pratico: è possibile proteggere in modo sicuro un'operazione sul lato destro con un controllo Null sul lato sinistro, come illustrato nell'esempio precedente. Se items è null, && si ferma lì — items.Count non viene mai chiamato, quindi non viene generata alcuna NullReferenceException.

Operatore condizionale ?:

L'operatore condizionale (detto anche operatore ternario ) valuta una delle due espressioni in base a una condizione:

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

L'operatore ?: valuta sempre esattamente un ramo, ovvero il lato che non corrisponde alla condizione non viene mai valutato. Questo rende sicuro usare un'espressione da un lato che genererebbe un errore con altri input, purché la condizione la protegga correttamente.

Usa ?: per scelte semplici, in linea. Per le condizioni o i blocchi multi-way del codice, un'istruzione if/else è in genere più chiara.

Operatori di assegnazione

L'operatore = di assegnazione semplice archivia un valore in una variabile:

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

L'assegnazione in C# è associativa da destra, il che significa che a = b = c = 0 viene valutata da destra a sinistra: c riceve 0, quindi b riceve 0, quindi a riceve 0.

Assegnazione composta

Gli operatori di assegnazione composti combinano un'operazione binaria con l'assegnazione:

Operatore 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

L'assegnazione composta è più di una semplice forma abbreviata. Valuta il membro sinistro esattamente una volta e quindi riconverte il risultato nel tipo del membro sinistro. Questo è importante quando il lato sinistro ha effetti collaterali (come un indicizzatore di array) ed è per questo che l'assegnazione composta su una variabile byte viene compilata senza un cast esplicito, mentre la forma estesa no:

// 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 viene compilato perché il compilatore inserisce automaticamente la conversione di tipo narrowing, ovvero il risultato, 210, rientra nell'intervallo byte compreso tra 0 e 255. small = small + 10 richiederebbe un cast esplicito (byte) , perché l'aritmetica promuove entrambi gli operandi in int.

Altri operatori C#

Questo articolo illustra gli operatori che si incontreranno più nel codice quotidiano. Il linguaggio C# include più operatori utili in scenari specifici:

  • Operatori di spostamento (<<, >>, >>>) : spostano i bit di un valore intero a sinistra o a destra in base a un numero specificato di posizioni. Operatori logici bit per bit e integer (&, |, ^, ~) - combinare o invertire valori integer un bit alla volta, utili in flag, maschere e codice di basso livello: operatori bit per bit e shift
  • checked e unchecked — controllare se l'overflow integer genera un'eccezione (checked) o esegue il wrapping in modo invisibile all'utente (unchecked): selezionato e deselezionato
  • Operatori null (??, ??=, ?., ?[]) — gestiscono in modo sicuro i valori null fornendo valori predefiniti o interrompendo l'accesso ai membri: Operatori null
  • Operatori di test e conversione dei tipi (is, as, typeof, cast (T)) - controllare o convertire il tipo di runtime di un valore: operatori di test dei tipi e cast
  • Operatori di intervallo e di indice (.., ^) — consentono di creare intervalli e indici relativi alla fine per la suddivisione di array e span: Operatori di accesso ai membri e condizionali rispetto a null
  • Assegnazione di decostruzione : decomprimere una tupla o un tipo in singole variabili in una singola espressione: Decostruzione di tuple e altri tipi

Vedere anche