Istruzioni di iterazione

Tip

Questo articolo fa parte della sezione Nozioni fondamentali per gli sviluppatori che già conoscono almeno un linguaggio di programmazione e stanno imparando C#. Se non si ha familiarità con la programmazione, iniziare prima con le esercitazioni introduttive . Per la sintassi completa, vedere istruzioni di iterazione nel riferimento al linguaggio.

Venire da un'altra lingua? Tutti e quattro i cicli C# (foreach, while,do-while e for) hanno equivalenti diretti in Java, C++e JavaScript. Si usa foreach più spesso. Esegue l'iterazione di una raccolta senza un indice, ad esempio Java avanzato for o JavaScript.for...of

Le istruzioni di iterazione eseguono ripetutamente un blocco di codice. Ogni passaggio del blocco è un'iterazione e un blocco ripetuto è un ciclo. C# offre quattro cicli. Iniziare con foreach per le raccolte, usare while edo-whilequando una condizione controlla la ripetizione e usare for quando è necessario un indice esplicito.

Iterare una raccolta con foreach

L'istruzione foreach esegue il corpo una volta per ogni elemento di una raccolta, in ordine. È la scelta più comune per la lettura di una raccolta perché non si gestisce un indice o un controllo dei limiti. L'istruzione foreach impedisce i tipici errori off-by-one:

string[] names = ["Ana", "Ben", "Cleo"];

// foreach reads each element in order. It's the default loop for
// collections: no index to manage and no off-by-one mistakes.
foreach (string name in names)
{
    Console.WriteLine(name); // => Ana, then Ben, then Cleo
}

foreach funziona con qualsiasi tipo riconosciuto da C# come sequenza, incluse matrici, List<T>e Dictionary<TKey,TValue>. La variabile di iterazione (name nell'esempio precedente) è di sola lettura, quindi non è possibile riassegnarla all'interno del ciclo.

Il corpo di un ciclo è una singola istruzione, ad esempio un'assegnazione o una chiamata al metodo. Un blocco di istruzioni è esso stesso una singola istruzione che racchiude zero o più istruzioni tra parentesi graffe ({ }).

Molti standard di codifica consigliano di racchiudere il corpo del ciclo tra parentesi graffe, anche per una singola istruzione. Le parentesi graffe esplicitano l'ambito. Evita un errore comune: aggiungere successivamente una seconda riga che si pensa venga eseguita a ogni iterazione, ma che invece viene eseguita una sola volta dopo il ciclo. Solo le parentesi graffe decidono quali istruzioni appartengono al ciclo. C# non considera gli spazi vuoti come significativi, quindi il rientro da solo non lo fa mai. Le parentesi graffe sono valide anche attorno a una sola riga: il blocco è l'unica istruzione che il ciclo ripete. Indenta il codice per renderlo leggibile, ma affidati alle parentesi graffe per delimitare il blocco.

Ripeti finché una condizione è vera con while

Un while ciclo controlla la condizione booleana prima di ogni iterazione. Se la condizione è false all'inizio, il corpo non viene mai eseguito, quindi un ciclo while si esegue zero o più volte:

int countdown = 3;

// A while loop checks its condition before each iteration, so the body
// runs zero or more times.
while (countdown > 0)
{
    Console.WriteLine(countdown); // => 3, then 2, then 1
    countdown--;
}

Assicurarsi che qualcosa all'interno del ciclo modifichi la condizione. Nell'esempio precedente, countdown-- alla fine rende la condizione false. Un ciclo la cui condizione non diventa mai false viene eseguito all'infinito.

Eseguire il blocco di codice almeno una volta con do-while

Un do-while ciclo controlla la condizione dopo ogni iterazione, quindi il corpo del ciclo viene sempre eseguito almeno una volta. Usarlo quando il primo passaggio deve verificarsi prima di poter valutare la condizione, ad esempio richiedendo l'input e convalidandolo:

int attempts = 0;

// A do-while loop runs its body once, then checks the condition. Use it
// when the body must run at least one time.
do
{
    attempts++;
    Console.WriteLine($"Attempt {attempts}"); // => Attempt 1, then Attempt 2, then Attempt 3
}
while (attempts < 3);

Conteggia con for

Un'istruzione for loop contiene tre parti: un inizializzatore che viene eseguito una volta prima del ciclo, una condizione verificata prima di ogni iterazione e un iteratore che viene eseguito dopo ogni iterazione. Usare for quando è necessario l'indice stesso, non solo gli elementi. In genere, è necessario l'indice quando si desidera modificare l'elemento anziché leggerne il valore.

// A for loop fits when you need an explicit index. The three parts are
// the initializer, the condition, and the iterator.
for (int i = 0; i < 3; i++)
{
    Console.WriteLine(i); // => 0, then 1, then 2
}

Quando si leggono solo gli elementi, quando non si usano le posizioni o si assegnano nuovi valori, preferire foreach. Indica più chiaramente la finalità ed evita l'aritmetica dell'indice.

Uscire o saltare con break e continue

Due istruzioni di salto consentono un controllo più preciso in qualsiasi ciclo. L'istruzione break esce immediatamente dal ciclo, ignorando eventuali iterazioni rimanenti:

int[] numbers = [2, 4, 7, 8];

// break stops the loop immediately, skipping any remaining elements.
foreach (int number in numbers)
{
    if (number % 2 != 0)
    {
        Console.WriteLine($"First odd number: {number}"); // => First odd number: 7
        break;
    }
}

L'istruzione continue ignora il resto dell'iterazione corrente e passa a quella successiva:

int[] values = [1, 2, 3, 4, 5];

// continue skips the rest of the current iteration and moves to the next.
foreach (int value in values)
{
    if (value % 2 == 0)
    {
        continue; // skip even numbers
    }

    Console.WriteLine(value); // => 1, then 3, then 5
}

Eseguire l'iterazione di un flusso asincrono con await foreach

Un flusso asincrono è un lettore che usa un'attività asincrona per produrre ogni elemento successivo. C# lo rappresenta con l'interfaccia IAsyncEnumerable<T> . I dati che arrivano nel tempo, ad esempio pagine da un'API Web o righe da un database, si adattano a questo modello: il recupero dell'elemento successivo è un'operazione awaitable anziché una restituzione immediata.

Per usare un flusso asincrono, inserire la parola chiave await prima di foreach. Ogni iterazione attende l'elemento successivo, quindi il ciclo viene sospeso mentre l'elemento viene generato invece di bloccare il thread:

private static async Task AwaitForeachExample()
{
    // await foreach consumes an asynchronous stream. Each iteration can
    // suspend while the next element is produced.
    await foreach (int value in GenerateAsync())
    {
        Console.WriteLine(value); // => 0, then 1, then 2
    }
}

private static async IAsyncEnumerable<int> GenerateAsync()
{
    for (int i = 0; i < 3; i++)
    {
        await Task.Delay(1); // stand-in for real asynchronous work
        yield return i;
    }
}

I flussi asincroni si basano su async e await. Per una procedura dettagliata completa, vedere Generare e usare flussi asincroni.

Vedere anche