Menu

Ciclo while e do while in C#: sintassi, lettura dell'input e trappole

Come funzionano i cicli while e do while in C#: la condizione controllata prima o dopo il corpo, leggere l'input fino a un valore sentinella o fino alla fine, while (true) con break e gli errori da ciclo infinito in cui cadono in molti.

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

Un ciclo while ripete il suo corpo finché una condizione resta vera. Usalo quando non sai in anticipo quante iterazioni ti servono: vai avanti finché un saldo raggiunge un obiettivo, una coda si svuota o l'input finisce.

Output:

Year 1: 1070.00
Year 2: 1144.90
Year 3: 1225.04
Year 4: 1310.80
Year 5: 1402.55
Year 6: 1500.73
Goal reached after 6 years

La condizione viene controllata prima di ogni iterazione, compresa la prima. Se savings avesse già raggiunto l'obiettivo, il corpo non verrebbe mai eseguito e il programma stamperebbe "after 0 years". Il ciclo dipende dal fatto che il corpo cambi qualcosa che la condizione legge; qui savings cresce ogni anno finché savings < goal diventa falsa.

do while: eseguire almeno una volta

Un ciclo do while sposta il test alla fine, quindi il corpo viene sempre eseguito almeno una volta:

Output:

Attempt 1: got 7
Attempt 2: got 4
Attempt 3: got 1
Done after 3 attempts

Il primo valore di code esiste solo dopo che il corpo è stato eseguito una volta, ed è proprio il caso in cui un do while è adatto. Due dettagli di sintassi ingannano spesso:

  • Il punto e virgola dopo while (code != 1) è obbligatorio. Ometterlo è un errore di compilazione.
  • Le variabili usate nella condizione devono essere dichiarate prima di do. Una variabile dichiarata dentro le graffe è fuori scope quando viene valutata la condizione, quindi do { int code = ...; } while (code != 1); non compila.

Lo stesso ciclo scritto con while richiede una prima chiamata duplicata prima del ciclo oppure un valore iniziale fittizio che forzi il primo passaggio. do while esprime direttamente "prova, poi decidi se riprovare".

Leggere l'input fino a una sentinella

Leggere righe fino a una parola di arresto, o fino alla fine dell'input, è il classico ciclo while da manuale. Console.ReadLine() restituisce null quando non c'è più input, quindi verifica entrambe le cose:

Output:

skipping "abc": not a number
3 amounts, total 49.50

(line = Console.ReadLine()) != null assegna la riga successiva e la verifica in un'unica espressione; le parentesi attorno all'assegnazione sono obbligatorie perché != ha precedenza più alta di =. Il ciclo si ferma a done, quindi il 99 che segue non viene mai letto. decimal.TryParse rifiuta abc senza lanciare eccezioni, e continue passa alla riga successiva. Senza il controllo su null, arrivati alla fine dell'input si passerebbe null a TryParse all'infinito, perché ReadLine continua a restituire null.

Un do while è adatto allo schema chiedi e valida, in cui la richiesta deve comparire almeno una volta:

Output:

Enter your age:
"-4" is not a valid age
Enter your age:
"fifteen" is not a valid age
Enter your age:
Age accepted: 15

La richiesta è dentro il ciclo, quindi si ripete dopo ogni risposta rifiutata, e age è dichiarata prima di do così il codice dopo il ciclo può usarla. Con un utente alla tastiera, ogni risposta compare nella console dopo la sua richiesta; con l'input reindirizzato, come qui, viene mostrato solo l'output del programma.

while (true) con break

A volte il punto di uscita naturale sta a metà del corpo, dopo un po' di lavoro e prima del resto. Scrivi il ciclo come while (true) ed esci con break:

Output:

processing: resize photo
processing: send email
stop signal received
1 job(s) left in the queue

Dequeue prende il prossimo lavoro dalla testa della coda, e il ciclo esce appena vede il marcatore di arresto, quindi l'ultimo lavoro resta in coda. while (true) non è un code smell quando l'uscita è chiara e vicina all'inizio del corpo. Lo diventa quando ci sono diversi break sparsi in un corpo lungo; in quel caso una condizione bool con un nome o un metodo con return sono più facili da seguire. La pagina su break e continue spiega come uscire da cicli annidati.

Cicli che non finiscono mai

Un ciclo infinito involontario ha una di tre cause.

Il corpo non cambia mai la condizione. Il contatore non viene mai incrementato, oppure viene incrementata la variabile sbagliata:

int i = 0;
while (i < 5)
{
    Console.WriteLine(i);   // i++ forgotten: prints 0 forever
}

Un punto e virgola dopo la condizione. Il punto e virgola è l'intero corpo del ciclo, e il blocco sotto viene eseguito dopo il ciclo (che non finisce mai):

while (retries < 3);        // warning CS0642: Possible mistaken empty statement
{
    retries++;
}

Una condizione che salta oltre il suo obiettivo. Verificare != quando la variabile avanza a passi può scavalcare il valore di arresto. while (x != 10) x += 3; passa per 0, 3, 6, 9, 12 e va dritto oltre 10 (cade su 10 solo per caso, dopo che l'int va in overflow e ricomincia da capo miliardi di passi dopo). Verifica con < o > invece di != ogni volta che il passo non è 1. Lo stesso vale per i valori double, che raramente centrano un obiettivo esatto: while (x != 1.0) x += 0.1; non finisce mai.

Scegliere tra while, do while e for

SituazioneCiclo
Numero di iterazioni noto, oppure un indicefor
Ogni elemento di una collezioneforeach
Ripetere finché vale una condizione, anche zero voltewhile
Il corpo deve essere eseguito una volta prima che la condizione abbia sensodo while
Test di uscita a metà del corpowhile (true) con break

Qualsiasi ciclo for può essere riscritto come ciclo while e viceversa; la scelta dipende da quale dei due mette la logica del ciclo dove chi legge se la aspetta. Quando ci sono contatore, test e passo, for li tiene su una riga. Quando il ciclo è guidato da uno stato che cambia dentro il corpo, while lo rende esplicito.

Domande frequenti

Qual è la differenza tra while e do while in C#?

Un ciclo while controlla la condizione prima di ogni iterazione, quindi il suo corpo può essere eseguito zero volte. Un ciclo do { } while (condition); la controlla dopo ogni iterazione, quindi il suo corpo viene sempre eseguito almeno una volta. Usa do while quando il primo passaggio produce il valore che la condizione verifica, per esempio leggere un input e poi validarlo.

Qual è la sintassi del ciclo do while in C#?

do { body } while (condition);. Nota il punto e virgola dopo la parentesi di chiusura: è obbligatorio, e ometterlo è un errore di compilazione. La condizione può usare variabili assegnate nel corpo, ma devono essere dichiarate prima del do, perché le graffe del corpo sono uno scope separato.

Come si scrive while (true) in C#?

while (true) { ... } si ripete all'infinito finché qualcosa al suo interno non esce con break, return o un'eccezione. È la forma naturale quando il test di uscita sta a metà del corpo, per esempio leggere una riga e fermarsi quando è vuota.

Come leggo l'input finché l'utente non scrive una parola o l'input finisce in C#?

Fai il ciclo su Console.ReadLine(), che restituisce null alla fine dell'input: string line; while ((line = Console.ReadLine()) != null && line != "done") { ... }. L'assegnazione dentro la condizione legge la riga successiva e la verifica in un solo passo.

Perché il mio ciclo while in C# non finisce mai?

Di solito il corpo non cambia mai ciò che la condizione verifica (un i++ dimenticato), oppure c'è un punto e virgola subito dopo la condizione: while (count < 10); è un ciclo con il corpo vuoto che gira all'infinito. Il compilatore segnala il secondo caso con CS0642, Possible mistaken empty statement.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA