Menu

if else in C#: catene else if, condizioni ed errori comuni

Come funzionano if, else if ed else in C#: perché le condizioni devono essere bool, combinare i test con &&, || e !, quando contano le graffe, le guard clause al posto dell'annidamento profondo, e i bug di = al posto di == e del punto e virgola di troppo.

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

Un'istruzione if esegue un blocco di codice solo quando una condizione è vera. else if aggiunge altri test, ed else raccoglie tutto ciò che i test non hanno intercettato.

Output:

Order 120: shipping 0
Order 75.50: shipping 4.99
Order 20: shipping 9.99

Le condizioni vengono controllate dall'alto in basso e vince la prima vera. Un ordine di 120 corrisponde a >= 100 e non arriva mai al test >= 50, ed è per questo che la condizione più specifica va per prima. else if non è una parola chiave a sé: è un else il cui corpo è un altro if, quindi in C# non esistono elseif né elif.

Le condizioni devono essere bool

C# non tratta mai un numero, una stringa o un oggetto come vero o falso. L'espressione tra parentesi deve essere di tipo bool:

int count = 3;
if (count)          // error CS0029: Cannot implicitly convert type 'int' to 'bool'
if (count > 0)      // correct
if (name)           // error: a string is not a bool either
if (name != null)   // correct

Questo elimina un'intera famiglia di bug di C e JavaScript, dove 0, "" e null valgono in silenzio come falso. Il prezzo è che devi scrivere il confronto ogni volta, cosa che rende anche l'intenzione visibile a chi leggerà il codice dopo di te.

Combinare condizioni con &&, || e !

&& (e), || (o) e ! (non) combinano test booleani. && e || sono a corto circuito: il lato destro viene eseguito solo se il lato sinistro non ha già deciso la risposta.

Output:

maya: welcome
invalid or banned account
invalid or banned account
omar: age 9 rejected
invalid or banned account

La chiamata con null è sicura grazie al corto circuito: quando username != null è falso, username.Length non viene mai valutato, quindi nessuna NullReferenceException. Inverti l'ordine in username.Length >= 3 && username != null e la stessa chiamata va in crash.

&& ha precedenza su ||, quindi a || b && c significa a || (b && c). Aggiungi le parentesi ogni volta che mescoli i due; al compilatore non servono, a chi legge sì. Anche & e | a un solo carattere funzionano su bool, ma valutano sempre entrambi i lati. Usali solo quando il lato destro ha un effetto collaterale che deve avvenire.

Graffe e la regola dell'istruzione singola

Le graffe sono facoltative quando un ramo contiene una sola istruzione. Senza graffe, solo l'istruzione successiva appartiene all'if, qualunque cosa dica l'indentazione:

Output:

Reorder email sent
Stock: 12

"Reorder email sent" viene stampato anche se le scorte sono sufficienti: il secondo WriteLine è fuori dall'if, indentazione a parte. Una guardia su una riga come if (x == null) return; va bene senza graffe; tutto ciò che potrebbe crescere di una seconda riga dovrebbe averle.

Un bug simile è un punto e virgola subito dopo la condizione. if (stock < 5); chiude l'if con un'istruzione vuota, e il blocco che segue viene eseguito sempre. Il compilatore lo segnala con l'avviso CS0642, Possible mistaken empty statement, che conviene trattare come un errore.

= e == in una condizione

= assegna, == confronta. Per la maggior parte dei tipi, scrivere = per sbaglio non compila, perché il risultato di un'assegnazione è il valore assegnato e un int non è un bool:

int score = 10;
if (score = 100) { }   // error CS0029: Cannot implicitly convert type 'int' to 'bool'

Con una variabile bool l'assegnazione è essa stessa un bool, quindi l'errore di battitura compila:

Output:

Access granted
isAdmin is now True

La condizione ha sovrascritto isAdmin e poi ha testato il nuovo valore. Il compilatore dà un avviso (CS0665, l'assegnazione in un'espressione condizionale è sempre costante), ma la build riesce comunque. Per i bool, evita del tutto il confronto: if (isAdmin) e if (!isAdmin) si leggono meglio e non si possono sbagliare in questo modo.

if annidati o guard clause

Ogni livello di annidamento è un'altra condizione che chi legge deve tenere a mente. Quando ogni controllo rifiuta un input non valido, esci subito invece di annidare il percorso normale dentro tutti i controlli:

Output:

ordered 2 x keyboard
error: no product
error: quantity must be positive
error: insufficient balance

La versione annidata dello stesso metodo avrebbe tre livelli di if con il lavoro vero nel blocco più interno e tre rami else da sbrogliare in fondo. Con le guard clause, ogni regola sta accanto al suo messaggio di errore e l'ultima riga è il caso normale. La stessa idea funziona dentro i cicli con continue.

Dichiarare variabili nella condizione

Una condizione può dichiarare una variabile visibile dentro l'if. Le due forme comuni sono un out var da un metodo TryParse e il pattern di tipo con is:

Output:

42: positive number 42
abc: not a number
-7: number -7, not positive
string of length 5

TryParse restituisce false invece di lanciare un'eccezione con un input non valido, ed è per questo che si abbina in modo naturale a un if. La pagina sulla conversione di tipo tratta in dettaglio i metodi di parsing.

Quando usare qualcos'altro

  • Scegliere uno tra due valori: l'operatore condizionale condition ? a : b è più breve di un if che assegna in entrambi i rami.
  • Molti rami su un unico valore: un'istruzione switch confronta un valore con casi costanti ed è più facile da scorrere di dieci righe else if.
  • Associare un valore a un risultato: una ricerca in un Dictionary sostituisce una lunga catena quando i rami differiscono solo nei dati.

Domande frequenti

Come si scrive else if in C#?

Scrivi else if come due parole: if (a) { ... } else if (b) { ... } else { ... }. Le condizioni vengono testate dall'alto in basso e viene eseguito solo il primo ramo vero, quindi metti per primo il test più specifico. Non esiste una parola chiave elif o elseif.

Perché non posso scrivere if (count) in C#?

Una condizione in C# deve essere di tipo bool. A differenza di C e JavaScript, un int, una stringa o un oggetto non vengono mai convertiti in vero o falso, quindi if (count) fallisce con CS0029, Cannot implicitly convert type 'int' to 'bool'. Scrivi il test che intendi: if (count > 0) oppure if (name != null).

Che differenza c'è tra && e & in una condizione if?

&& è a corto circuito: se il lato sinistro è falso, il lato destro non viene mai valutato, ed è questo che rende sicuro s != null && s.Length > 0. & su due bool valuta entrambi i lati ogni volta. Lo stesso vale per || e |. Usa && e || nelle condizioni, a meno che non ti serva l'effetto collaterale del lato destro.

Le graffe sono obbligatorie in un if in C#?

No. Senza graffe l'if controlla esattamente un'istruzione. Indentare una seconda riga non la aggiunge al ramo, ed è una classica fonte di bug, quindi la maggior parte delle guide di stile consiglia le graffe su ogni ramo.

Posso scrivere un if else su una riga in C#?

Per scegliere un valore, usa l'operatore condizionale: string label = score >= 50 ? "pass" : "fail";. Per eseguire istruzioni, if (x) DoA(); else DoB(); su una riga è valido, ma è più difficile da leggere e da estendere rispetto a un blocco con le graffe.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA