Menu

Ciclo for in C#: sintassi, conto alla rovescia, cicli annidati e trappole

Come funziona il ciclo for in C#: inizializzatore, condizione e iteratore, contare all'indietro e a passi, più variabili di ciclo, scorrere gli array per indice, cicli annidati, for(;;) e i bug off-by-one e da rimozione durante l'iterazione.

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

Un ciclo for ripete un blocco finché una condizione è vera, con l'impostazione iniziale e il passo scritti nell'intestazione del ciclo. È il ciclo da usare quando conti: un numero fisso di ripetizioni, o ogni indice di un array.

Output:

Lap 1 of 5
Lap 2 of 5
Lap 3 of 5
Lap 4 of 5
Lap 5 of 5

Come vengono eseguite le tre parti

for (initializer; condition; iterator) viene eseguito in un ordine fisso:

  1. L'inizializzatore (int i = 1) viene eseguito una volta, prima di tutto il resto.
  2. La condizione (i <= 5) viene controllata. Se è falsa, il ciclo termina e il corpo non viene più eseguito.
  3. Viene eseguito il corpo.
  4. Viene eseguito l'iteratore (i++), poi il controllo torna al passo 2.

Dato che la condizione viene controllata prima di ogni iterazione, un ciclo for può essere eseguito zero volte: for (int i = 10; i < 5; i++) non entra mai nel corpo. Il ciclo qui sopra si comporta come questo ciclo while:

int i = 1;
while (i <= 5)
{
    Console.WriteLine($"Lap {i} of 5");
    i++;
}

Le due forme differiscono per un dettaglio: continue dentro il corpo di un for esegue comunque l'iteratore, mentre in questa versione while salterebbe i++, quindi il ciclo testerebbe di nuovo lo stesso i e potrebbe girare all'infinito. Una variabile dichiarata nell'inizializzatore esiste solo dentro il ciclo. Se ti serve il suo valore finale dopo, dichiarala prima del ciclo: int i; for (i = 0; ...; i++) { }.

Contare all'indietro e a passi

L'iteratore può essere qualsiasi espressione. Decrementa per contare all'indietro, aggiungi più di uno per avanzare a passi, oppure moltiplica per una sequenza geometrica:

Output:

3... 2... 1... liftoff
 00 15 30 45
 1 10 100 1000

Assicurati che l'iteratore si muova verso il punto in cui la condizione diventa falsa. for (int i = 0; i < 10; i--) si allontana da 10 e si ferma solo quando i va in overflow dopo due miliardi di iterazioni.

Scorrere un array per indice

Gli array espongono Length, le liste espongono Count, e gli indici validi vanno da 0 a quel valore meno uno. L'indice ti dà qualcosa che foreach non ti dà: l'accesso alla posizione, agli elementi vicini e la possibilità di riscrivere.

Output:

#1: 17.99
#2: 4.50
#3: 38.25
#4: 7.43
item 1 vs item 0: down
item 2 vs item 1: up
item 3 vs item 2: down

L'ultimo ciclo parte da 1 perché confronta ogni elemento con quello precedente. La pagina sugli array spiega come creare, ordinare e copiare gli array.

Più variabili di ciclo

L'inizializzatore può dichiarare più variabili dello stesso tipo, e l'iteratore può aggiornarne più d'una, separate da virgole. L'uso classico sono due indici che si vengono incontro:

Output:

racecar is a palindrome: True
9, 1, 8, 3

In una singola dichiarazione le variabili devono avere lo stesso tipo: int left = 0, right = 10 funziona, int i = 0, string s = "" no.

Cicli annidati

Un ciclo dentro un ciclo esegue tutto il suo intervallo una volta per ogni iterazione del ciclo esterno. Tabelle e griglie sono il caso tipico:

Output:

   1   2   3   4
   2   4   6   8
   3   6   9  12
   4   8  12  16

Il corpo interno viene eseguito 16 volte (4 × 4). Annidare moltiplica il lavoro in fretta: due cicli annidati su 10.000 elementi ciascuno fanno 100 milioni di iterazioni. Un break nel ciclo interno esce solo da quello interno; la pagina su break e continue mostra come uscire da entrambi.

Cicli infiniti: for(;;)

Tutte e tre le parti dell'intestazione sono facoltative. Senza condizione, il ciclo continua finché qualcosa al suo interno non lo fa uscire:

Output:

Month 1: 70 left
Month 2: 40 left
Month 3: 10 left
Overdrawn in month 4: -20

for (;;) e while (true) vengono compilati allo stesso modo; scegli quello che usa il tuo codice.

Errori off-by-one

Il bug più comune con for è una condizione che va un passo troppo in là:

Output:

Ada
Grace
Linus
IndexOutOfRangeException at index 3

Un array di 3 elementi ha gli indici 0, 1 e 2, quindi i < names.Length è la condizione giusta. Il bug speculare è contare all'indietro con i > 0, che non visita mai l'indice 0. Su una List<T>, lo stesso errore lancia invece ArgumentOutOfRangeException.

Rimuovere elementi durante il ciclo

Rimuovere da una List<T> dentro un ciclo in avanti sposta ogni elemento successivo di una posizione indietro, quindi il ciclo salta l'elemento subito dopo ogni rimozione:

Output:

forward:  5, 0, 3, 7
backward: 5, 3, 7

Il ciclo in avanti ha lasciato uno 0: dopo aver rimosso l'indice 1, il secondo zero si è spostato all'indice 1, ma i++ era già passato all'indice 2. Scorrere all'indietro evita il problema perché le rimozioni spostano solo elementi che hai già visitato. Come alternativa in una riga, stock.RemoveAll(x => x == 0) fa la stessa cosa. Un ciclo foreach non permette proprio la rimozione: lancia InvalidOperationException.

Contatori in virgola mobile

Evita contatori di tipo double testati con l'uguaglianza. 0.1 non ha una rappresentazione binaria esatta, quindi sommarlo dieci volte non dà esattamente 1.0:

for (double x = 0; x != 1.0; x += 0.1)   // never ends: x skips past 1.0
{
}

Conta invece con un intero e calcola il valore a partire da quello: for (int i = 0; i <= 10; i++) { double x = i / 10.0; }.

for o foreach

EsigenzaUsa
Leggere ogni elemento in ordineforeach
L'indice, o gli elementi vicini (i - 1, i + 1)for
Assegnare a un elemento di un arrayfor
Avanzare di 2, andare all'indietrofor
Rimuovere da una List durante il ciclofor, all'indietro
Una collezione senza indicizzatore (HashSet, Dictionary, risultati LINQ)foreach
Ripetere qualcosa N volte, senza collezionefor

Sugli array i due cicli vengono compilati in codice praticamente identico, quindi scegli in base alla leggibilità, non alla velocità.

Domande frequenti

Qual è la sintassi del ciclo for in C#?

for (initializer; condition; iterator) { body }, per esempio for (int i = 0; i < 5; i++) { ... }. L'inizializzatore viene eseguito una volta, la condizione viene controllata prima di ogni iterazione e l'iteratore viene eseguito dopo ogni iterazione. Il ciclo termina la prima volta che la condizione è falsa.

Come faccio un ciclo all'indietro in C#?

Parti dall'ultimo indice e decrementa: for (int i = items.Length - 1; i >= 0; i--). Nota >= 0, non > 0, altrimenti salti il primo elemento. Scorrere all'indietro è anche il modo sicuro per rimuovere elementi da una List durante l'iterazione.

Quando conviene usare for invece di foreach in C#?

Usa for quando ti serve l'indice (per confrontare elementi vicini, riscrivere in un array o avanzare di più di uno), quando conti invece di percorrere una collezione, o quando rimuovi elementi da una lista. Usa foreach per leggere ogni elemento in ordine; è più breve e non ha un indice da sbagliare.

Come scrivo un ciclo infinito in C#?

for (;;) { ... } oppure while (true) { ... }. Tutte e tre le parti dell'intestazione di un for sono facoltative, e una condizione mancante vale come vera. Esci dal ciclo con break o return quando la tua condizione di uscita è soddisfatta.

Perché ottengo IndexOutOfRangeException nel mio ciclo for?

La condizione lascia che l'indice raggiunga la lunghezza: i <= array.Length visita array[array.Length], uno oltre l'ultimo elemento. Gli indici validi vanno da 0 a Length - 1, quindi la condizione deve essere i < array.Length (oppure i < list.Count per una List, che invece lancia ArgumentOutOfRangeException).

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA