Un'istruzione switch confronta un valore con un elenco di etichette case costanti ed esegue la sezione che corrisponde. In C# ogni sezione deve terminare con break (o un'altra istruzione che ne esce), quindi un case non può mai proseguire silenziosamente nel successivo.
Output:
200: OK
302: redirect
404: not found
500: unhandled status
Il valore tra parentesi viene valutato una volta e confrontato con ogni etichetta. default viene eseguito quando non corrisponde nulla; è facoltativo e può comparire in qualsiasi punto dell'elenco, anche se di solito va per ultimo. Se non c'è corrispondenza e manca default, l'intero switch non fa nulla.
Ogni sezione ha bisogno di un break
In C, C++, Java e JavaScript, dimenticare break fa scivolare l'esecuzione nel case successivo. C# trasforma quell'errore in un errore di compilazione:
switch (level)
{
case 1:
Console.WriteLine("read");
// error CS0163: Control cannot fall through from one case label ('case 1:') to another
case 2:
Console.WriteLine("write");
break;
}
La regola è che la fine dell'elenco di istruzioni di una sezione non deve essere raggiungibile. break è il modo abituale per rispettarla, ma funziona qualsiasi cosa che esca dalla sezione:
returnesce dall'intero metodo, spesso la forma più pulita dentro un piccolo metodo di supporto.throwlancia un'eccezione per un valore che non dovrebbe mai presentarsi.continuesalta all'iterazione successiva quando loswitchsi trova dentro un ciclo.goto caseegoto defaultsaltano di proposito a un'altra sezione.
L'ultima sezione non fa eccezione. Un default: in fondo senza break fallisce con CS8070, Control cannot fall out of switch from final case label ('default:').
Più valori in un solo case
Le etichette senza istruzioni tra di loro condividono la sezione sottostante. È l'unico tipo di passaggio da un case all'altro che C# consente, perché un'etichetta vuota non può nascondere codice:
Output:
'O' is a vowel
'k' is a consonant or symbol
' ' is a space
'7' is a digit
Qui ogni sezione termina con return, quindi non serve break (un break dopo un return sarebbe codice irraggiungibile). Due etichette con lo stesso valore sono un errore, CS0152 The switch statement contains multiple cases with the label value '1', e ogni etichetta deve essere una costante nota in fase di compilazione: una variabile in un case fallisce con "a constant value is expected".
Switch su una stringa
Dopo gli interi, le stringhe sono il valore più comune in uno switch. Il confronto è ordinale e distingue maiuscole e minuscole, quindi normalizza l'input prima dello switch:
Output:
Starting the server
Stopping the server
Server is running
Unknown command: restart
Unknown command: (none)
Senza ToLowerInvariant, "STOP" finirebbe in default. Fare lo switch su un valore null è lecito: non corrisponde a nessuna costante stringa e finisce in default, quindi lo switch in sé non lancia mai eccezioni.
Switch su un enum
Gli enum sono ciò per cui switch è stato pensato: un insieme chiuso di valori con nome.
Output:
Pending: wait for payment
Paid: pack and ship
Shipped: track the parcel
Delivered: nothing, order is closed
Cancelled: nothing, order is closed
ArgumentOutOfRangeException for value 42
Il compilatore non verifica che un'istruzione switch copra ogni membro dell'enum, e una variabile enum può contenere qualsiasi intero del suo tipo sottostante ((OrderStatus)42 è lecito). Il default che lancia un'eccezione trasforma un case dimenticato in un errore immediato ed evidente invece che in un'operazione nulla e silenziosa.
goto case e goto default
Quando un case deve davvero continuare in un altro, C# ti obbliga a scrivere il salto:
Output:
admin:
manage users
edit posts
read posts
editor:
edit posts
read posts
guest:
read posts
goto case accetta una costante che deve corrispondere a una delle etichette dello stesso switch. Nel codice reale è raro: il comportamento condiviso di solito è più chiaro come metodo chiamato da più case. Resta comunque la risposta a "come faccio il fall through in C#".
Variabili dentro i case
L'intero blocco switch è un unico ambito, quindi due case non possono dichiarare una variabile con lo stesso nome:
switch (shape)
{
case "square":
int area = side * side;
break;
case "rectangle":
int area = width * height; // error CS0128: A local variable or function named 'area' is already defined in this scope
break;
}
Racchiudi il corpo di ogni sezione tra graffe per dargli un ambito proprio: case "square": { int area = side * side; ... break; }.
break dentro un ciclo
break in un case esce dallo switch, non dal ciclo che lo contiene. continue fa l'opposto: uno switch non è un ciclo, quindi continue passa all'iterazione successiva del ciclo.
Output:
end marker found
Total: 134
Il ciclo è andato avanti dopo "end" e ha aggiunto 99. Per fermare il ciclo dall'interno di uno switch, imposta un flag e controllalo dopo lo switch, oppure sposta il ciclo in un metodo e usa return. La pagina su break e continue confronta le opzioni.
Etichette case con pattern e when
Da C# 7.0, un'etichetta case può essere un pattern invece di una costante: può verificare il tipo del valore e aggiungere una condizione when. Questa forma è comune nel codice C# moderno:
static string Describe(object value)
{
switch (value)
{
case null:
return "nothing";
case int n when n < 0:
return $"negative int {n}";
case int n:
return $"int {n}";
case string s when s.Length == 0:
return "empty string";
case string s:
return $"string \"{s}\"";
default:
return $"other: {value.GetType().Name}";
}
}
Con i pattern l'ordine conta: vince il primo case che corrisponde, e il compilatore segnala un errore quando un case non può mai essere raggiunto perché uno precedente lo copre già (mettere case int n: sopra case int n when n < 0: fallisce). La clausola when funziona anche sui case costanti: case 0 when isAdmin:. C# 9 ha aggiunto i pattern relazionali e logici, quindi un case può essere case >= 90: o case 'a' or 'e' or 'i':.
In C# 7 senza pattern, lo stesso smistamento per tipo è una catena di if con il pattern di tipo is:
Output:
negative int -3
int 12
empty string
string "hi"
other: Decimal
nothing
Quando ogni ramo di uno switch si limita a calcolare un valore, la switch expression è ancora più breve: var grade = score switch { >= 90 => "A", >= 80 => "B", _ => "C" };. La pagina sulla switch expression ne copre la sintassi, i pattern e le regole di esaustività.
Su cosa puoi fare lo switch
| Tipo di valore | Case costanti | Note |
|---|---|---|
int, long, byte e altri interi | sì | il caso classico |
char | sì | case 'a': |
string | sì | ordinale, distingue maiuscole e minuscole, null va in default |
bool | sì | lecito, ma un if è più chiaro |
qualsiasi enum | sì | nessun controllo di esaustività |
double, decimal | da C# 7 | solo costanti; gli intervalli richiedono i pattern relazionali di C# 9 |
| qualsiasi altro tipo | da C# 7 | tramite pattern di tipo, case Circle c: |
Domande frequenti
Il break è obbligatorio in uno switch di C#?
Ogni sezione case deve terminare con un'istruzione che ne esce: di solito break, ma valgono anche return, throw, continue (dentro un ciclo) o goto case. Scivolare dalla fine di una sezione nella successiva è un errore di compilazione, CS0163 Control cannot fall through from one case label ('case 1:') to another, e questo vale anche per l'ultima sezione.
Come gestisco più valori in un solo case di uno switch in C#?
Impila le etichette senza nulla in mezzo: case "sat": case "sun": Console.WriteLine("weekend"); break;. Le etichette case vuote possono proseguire nell'etichetta successiva; solo una sezione che contiene istruzioni deve terminare con break. Da C# 9 in poi un case con pattern può anche scrivere case 1 or 2 or 3:.
Uno switch su una stringa in C# distingue maiuscole e minuscole?
Sì. I case di tipo stringa vengono confrontati con un'uguaglianza ordinale che distingue maiuscole e minuscole, quindi "Yes" non corrisponde a case "yes":. Normalizza prima il valore con switch (input.Trim().ToLowerInvariant()). Una stringa null non corrisponde a nessun case costante e finisce in default.
Uno switch in C# può proseguire nel case successivo come in C o Java?
Non implicitamente. Quando vuoi che un case continui in un altro, dichiaralo con goto case <value>; o goto default;. Così ogni passaggio da un case all'altro è visibile nel codice, ed è per questo che C# vieta la versione silenziosa.
Qual è la differenza tra istruzione switch e switch expression in C#?
Un'istruzione switch esegue istruzioni e richiede case, break e default. Una switch expression (C# 8 e successivi) produce un valore: var label = n switch { 0 => "zero", > 0 => "positive", _ => "negative" };. Usa l'espressione quando ogni ramo si limita a calcolare un risultato.