0Pricing
C# Academy · Lezione

IAsyncEnumerable e await foreach

Trasmetta dati in modo asincrono con IAsyncEnumerable, usi yield negli iteratori asincroni e consumi i dati con await foreach.

IAsyncEnumerable e await foreach è una lezione C# Academy gratuita su CoddyKit. Questa è la lezione 1 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento C# Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso C# Academy include 4 lezioni in totale.

Perché IAsyncEnumerable?

IAsyncEnumerable<T> (introdotto in C# 8 e .NET Core 3) consente lo streaming asincrono: gli elementi vengono restituiti uno alla volta man mano che diventano disponibili, senza caricarli prima tutti in memoria. Si pensi alla lettura, riga per riga, delle righe di un cursore di database o alla trasmissione in streaming delle risposte di un'API.

Scrivere un iteratore asincrono

Dichiari un metodo che restituisce IAsyncEnumerable<T> e utilizzi yield return all'interno di un metodo asincrono. Le parole chiave await e yield return coesistono naturalmente.

public async IAsyncEnumerable<int> GenerateNumbersAsync(int count)
{
    for (int i = 0; i < count; i++)
    {
        await Task.Delay(100); // simulate async work per item
        yield return i;
    }
}

Consumo con await foreach

Consumi un IAsyncEnumerable<T> con await foreach. Ogni iterazione attende in modo asincrono l'elemento successivo: consumatore e produttore si sincronizzano naturalmente.

await foreach (var number in GenerateNumbersAsync(10))
{
    Console.WriteLine(number);
}

// With cancellation:
var cts = new CancellationTokenSource();
await foreach (var n in GenerateNumbersAsync(100)
    .WithCancellation(cts.Token))
{
    if (n > 50) { cts.Cancel(); break; }
    Console.WriteLine(n);
}

Streaming da un database

EF Core supporta lo streaming dei risultati delle query con AsAsyncEnumerable(). In questo modo le righe vengono lette una alla volta, senza caricare l'intero set di risultati in memoria: un aspetto fondamentale per le tabelle di grandi dimensioni.

public async IAsyncEnumerable<Product> StreamProductsAsync(
    [EnumeratorCancellation] CancellationToken ct = default)
{
    await foreach (var product in _db.Products
        .Where(p => p.IsActive)
        .AsAsyncEnumerable()
        .WithCancellation(ct))
    {
        yield return product;
    }
}

Streaming delle risposte HTTP

Trasmetta in streaming le risposte di grandi dimensioni ai client senza memorizzare l'intero payload in un buffer. Il client riceve progressivamente i dati mentre il server continua l'elaborazione.

app.MapGet("/stream/products", (ProductRepository repo) =>
{
    // Returns an IAsyncEnumerable — ASP.NET Core streams it as JSON array
    return repo.StreamAllAsync();
});

// Or with custom serialization:
app.MapGet("/stream/events", async (HttpResponse response, EventStore store) =>
{
    response.ContentType = "application/json";
    await foreach (var e in store.StreamAsync())
    {
        await response.WriteAsJsonAsync(e);
        await response.Body.FlushAsync();
    }
});

L'attributo EnumeratorCancellation

Applichi [EnumeratorCancellation] al parametro CancellationToken di un iteratore asincrono, in modo che WithCancellation() sul lato consumatore propaghi il token al produttore.

public async IAsyncEnumerable<LogEntry> TailLogAsync(
    string logFile,
    [EnumeratorCancellation] CancellationToken ct = default)
{
    while (!ct.IsCancellationRequested)
    {
        var newLines = await ReadNewLinesAsync(logFile, ct);
        foreach (var line in newLines)
            yield return new LogEntry(line);
        await Task.Delay(500, ct);
    }
}

// Consumer:
await foreach (var entry in TailLogAsync("/var/log/app.log")
    .WithCancellation(cts.Token))
{
    Console.WriteLine(entry.Message);
}

LINQ su IAsyncEnumerable

Il pacchetto NuGet System.Linq.Async fornisce operatori LINQ asincroni (WhereAwait, SelectAwait, ToListAsync) per IAsyncEnumerable.

// dotnet add package System.Linq.Async

var expensiveProducts = await repo.StreamAllAsync()
    .WhereAwait(async p => await pricing.IsExpensiveAsync(p.Id))
    .SelectAwait(async p => await enricher.EnrichAsync(p))
    .Take(10)
    .ToListAsync();

// Or use built-in Where/Select that don't need async:
var names = repo.StreamAllAsync()
    .Where(p => p.IsActive)
    .Select(p => p.Name);

Unione di più flussi asincroni

Per elaborare più flussi asincroni contemporaneamente, utilizzi Channel come punto di fan-in: scriva da più produttori e legga da un singolo consumatore.

async IAsyncEnumerable<T> Merge<T>(
    params IAsyncEnumerable<T>[] streams)
{
    var channel = Channel.CreateUnbounded<T>();

    var producers = streams.Select(async stream =>
    {
        await foreach (var item in stream)
            await channel.Writer.WriteAsync(item);
    });

    _ = Task.WhenAll(producers)
            .ContinueWith(_ => channel.Writer.Complete());

    await foreach (var item in channel.Reader.ReadAllAsync())
        yield return item;
}

Prestazioni: buffering e streaming

Utilizzi sempre lo streaming durante l'elaborazione di dataset di grandi dimensioni. Caricare prima tutto in una lista (ToListAsync()) mantiene simultaneamente tutti i dati in memoria; lo streaming elabora un elemento alla volta.

// BAD: loads all 1M records into memory
var allOrders = await _db.Orders.ToListAsync();
foreach (var order in allOrders)
    await ProcessAsync(order);

// GOOD: processes one record at a time
await foreach (var order in _db.Orders.AsAsyncEnumerable())
    await ProcessAsync(order);
// Memory usage: O(1) vs O(n)

Gestione degli errori negli iteratori asincroni

Racchiuda l'intero await foreach in un blocco try/catch per gestire gli errori del produttore. Gli errori nell'iteratore vengono propagati al consumatore nel punto dell'iterazione corrente.

try
{
    await foreach (var item in RiskyStreamAsync())
    {
        await ProcessAsync(item);
    }
}
catch (HttpRequestException ex)
{
    Console.Error.WriteLine($"Stream error: {ex.Message}");
}
catch (OperationCanceledException)
{
    Console.WriteLine("Stream cancelled.");
}

Controllo rapido

Qual è il principale vantaggio in termini di memoria di IAsyncEnumerable rispetto alla restituzione di una List?

Riepilogo: IAsyncEnumerable e await foreach

Punti chiave:

  • IAsyncEnumerable<T>: trasmettere gli elementi in modo asincrono, uno alla volta
  • yield return nei metodi asincroni produce iteratori asincroni
  • await foreach: consumare il flusso con la cancellazione tramite .WithCancellation()
  • [EnumeratorCancellation]: propaga il token del consumatore all'iteratore
  • EF Core: utilizzare AsAsyncEnumerable() per query di grandi dimensioni efficienti in termini di memoria
  • Elaborare dataset di grandi dimensioni trasmettendo in streaming invece di utilizzare ToListAsync()

Domande Frequenti

La lezione «IAsyncEnumerable e await foreach» è gratuita?

Sì — il testo completo di «IAsyncEnumerable e await foreach» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso C# Academy, passa a CoddyKit PRO. Il corso C# Academy include 4 lezioni in totale.

Cosa imparerò in «IAsyncEnumerable e await foreach»?

Trasmetta dati in modo asincrono con IAsyncEnumerable, usi yield negli iteratori asincroni e consumi i dati con await foreach. Eserciti C# Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare C# Academy?

Non è richiesta alcuna esperienza precedente. C# Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 1 di 4.

Quanto tempo richiede la lezione «IAsyncEnumerable e await foreach»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione C# Academy?

Sì. Ogni lezione C# Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. IAsyncEnumerable e await foreach
  2. System.Threading.Channels
  3. ValueTask ed evitare allocazioni
  4. ConfigureAwait e contesto di sincronizzazione
← Torna a C# Academy