break termina un ciclo in anticipo. continue salta il resto dell'iterazione corrente e passa alla successiva. Entrambi si applicano al ciclo più interno che li contiene e funzionano allo stesso modo in for, foreach, while e do while.
Output:
process order of 45
skip invalid total 0
process order of 120
skip invalid total -5
process order of 80
test order found, stopping
after the loop
continue ha saltato la riga di elaborazione per 0 e -5. break ha fermato il ciclo a 999, quindi 60 non è mai stato visitato, e l'esecuzione è ripresa dalla prima istruzione dopo il ciclo.
break: fermarsi quando la risposta è nota
L'uso tipico di break è una ricerca: una volta trovato ciò che cercavi, non c'è motivo di guardare il resto.
Output:
first invalid email at index 1
L'incremento (i++) non viene eseguito dopo un break, quindi i conterrebbe ancora l'indice trovato se fosse dichiarata fuori dal ciclo. Per le ricerche semplici, una chiamata di libreria dice la stessa cosa: Array.FindIndex(emails, e => !e.Contains("@")) restituisce l'indice (o -1), e emails.Any(e => !e.Contains("@")) di LINQ risponde se ne esiste uno non valido.
continue: saltare all'iterazione successiva
continue è una clausola di guardia per i cicli. Invece di avvolgere il lavoro vero in un if, scarta in cima i casi che non ti interessano:
Output:
pen costs 1.20
mug costs 8.00
Dove va continue dipende dal ciclo. In un ciclo for esegue l'incremento e poi valuta la condizione. In un ciclo while salta direttamente alla condizione, il che porta a un bug classico:
int i = 0;
while (i < 10)
{
if (i % 2 == 0)
continue; // i++ below is skipped: i stays 0 forever
Console.WriteLine(i);
i++;
}
Aggiorna il contatore prima di qualsiasi continue, oppure usa un ciclo for, dove l'aggiornamento sta nell'intestazione e viene sempre eseguito.
break dentro uno switch dentro un ciclo
In uno switch, break termina la sezione del case. Quando quello switch si trova in un ciclo, il break appartiene allo switch, non al ciclo:
Output:
quit received
count = 2
Un semplice break nel case "quit" avrebbe terminato solo lo switch, il ciclo sarebbe andato avanti e count sarebbe stato 3. Il goto salta all'etichetta dopo il ciclo. Anche spostare il ciclo in un metodo e scrivere return in quel case funziona, e non richiede etichette.
Uscire da cicli annidati
C# non ha un break con etichetta come il break outer; di Java. Quando una ricerca attraversa due cicli annidati, un break nel ciclo interno termina solo quello interno; il ciclo esterno passa alla riga successiva. Ci sono tre soluzioni standard.
1. Un flag che il ciclo esterno controlla:
Output:
first free seat: row 1, seat 2
2. goto verso un'etichetta dopo entrambi i cicli. È l'unico caso in cui molte guide di stile per C# accettano goto, perché esprime esattamente "esci da entrambi i cicli":
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
if (seats[row, col] == 0)
{
Console.WriteLine($"first free seat: row {row}, seat {col}");
goto done;
}
}
}
done:
Console.WriteLine("search finished");
3. Un metodo con return, di solito la soluzione più pulita, perché la ricerca riceve un nome e può restituire il suo risultato:
Output:
first free seat: row 1, seat 2
C# 7.0 ha introdotto anche le funzioni locali, che ti permettono di mettere quel metodo di supporto dentro il metodo chiamante. Il pattern con parametro out usato qui è spiegato nella pagina su ref e out.
goto ed etichette
Un'etichetta è un identificatore seguito da due punti, e goto label; ci salta. Le regole sono abbastanza rigide da impedire gli abusi peggiori:
- L'etichetta deve trovarsi nello stesso metodo, nello stesso blocco del
gotoo in uno che lo contiene. Puoi saltare fuori dai blocchi ma mai dentro: saltare da fuori a un'etichetta nel corpo di un ciclo dà l'errore CS0159 (nessuna etichetta del genere nell'ambito dell'istruzione goto). - Non puoi uscire da un blocco
finallycongoto,breakocontinue. - Dentro uno
switch,goto case 3;egoto default;trasferiscono il controllo a un'altra sezione. È così che uno switch in C# esprime il fall-through, dato che cadere implicitamente nel case successivo è un errore di compilazione.
Con goto si possono anche costruire cicli (saltando all'indietro verso un'etichetta), ma un ciclo while dice la stessa cosa in una forma che ogni lettore riconosce. Tieni goto per uscire da cicli annidati e per le sezioni degli switch.
return, throw e i cicli
return esce dall'intero metodo, quindi esce anche da tutti i cicli al suo interno. throw fa lo stesso sollevando un'eccezione. Dentro un blocco try con un finally, break, continue, goto e return eseguono comunque il blocco finally prima che il controllo passi oltre, ed è questo che rende sicuro uscire da un ciclo che tiene aperto un file in un blocco using.
| Istruzione | Esce da | Prosegue da |
|---|---|---|
continue | l'iterazione corrente | l'iterazione successiva (incremento, poi condizione) |
break | il ciclo o lo switch più interno | l'istruzione successiva |
goto label | tutti i blocchi che lo contengono | l'etichetta |
return | il metodo | il chiamante |
throw | tutto fino a un catch corrispondente | il blocco catch |
Domande frequenti
Che differenza c'è tra break e continue in C#?
break termina subito il ciclo più interno (o lo switch); l'esecuzione prosegue dopo di esso. continue termina solo l'iterazione corrente e passa alla successiva: in un ciclo for viene eseguito poi l'incremento, in while e do while viene valutata la condizione, in foreach viene preso l'elemento successivo.
Come esco da cicli annidati in C#?
C# non ha un break con etichetta. Le tre opzioni sono: impostare un flag bool e controllarlo nel ciclo esterno, saltare oltre entrambi i cicli con goto verso un'etichetta, oppure spostare i cicli in un metodo e fare return. La versione con il metodo di solito è la più pulita, perché dà anche un nome e un valore di ritorno alla ricerca.
Un break dentro uno switch esce dal ciclo in C#?
No. Dentro uno switch che si trova in un ciclo, break termina la sezione dello switch e il ciclo continua. Per uscire dal ciclo da dentro un case, usa return, un flag controllato dopo lo switch, o goto verso un'etichetta dopo il ciclo. continue dentro un case invece passa all'iterazione successiva del ciclo.
goto è consentito in C#?
Sì. goto label; salta a label: nello stesso metodo, ma non può saltare dentro un blocco né uscire da un finally. Dentro uno switch, goto case value; e goto default; saltano tra le sezioni. Al di fuori dell'uscita da cicli annidati e del fall-through esplicito negli switch, goto raramente è la scelta più chiara.