CodeGym /Corsi /C# SELF /Gestione degli errori nelle operazioni asincrone (

Gestione degli errori nelle operazioni asincrone ( async/ await)

C# SELF
Livello 42 , Lezione 3
Disponibile

1. Introduzione

Iniziamo con una domanda importante: perché non è possibile fidarsi semplicemente dei metodi asincroni e sperare che tutto funzioni sempre alla perfezione? Le operazioni sui file spesso lanciano eccezioni — il file potrebbe essere stato eliminato, la memoria finita, permessi insufficienti o il file bloccato. Nel codice sincrono cattureresti i problemi nel solito blocco try-catch. Nel codice asincrono la filosofia è la stessa, ma ci sono sfumature: l'errore può verificarsi non immediatamente alla chiamata del metodo, ma più tardi, quando l'operazione viene effettivamente eseguita.

Momento in cui nasce l'errore

Nel codice sincrono, leggendo con StreamReader.Read(), l'eccezione verrà sollevata direttamente sulla riga di chiamata — la prendi nel catch e tutto ok.
Nel codice asincrono (await stream.ReadAsync()) l'errore cadrà non al momento dell'avvio dell'operazione, ma proprio al await — quando il Task termina con un errore. Se dimentichi di mettere il await, l'errore può rimanere «invisibile» per un po'.

2. Come catturare le eccezioni nei metodi asincroni

Vediamo subito lo schema tipico:


try
{
    using FileStream fs = new FileStream("myfile.txt", FileMode.Open);
    byte[] buffer = new byte[1024];
    int bytesRead = await fs.ReadAsync(buffer, 0, buffer.Length);
    // Ulteriore elaborazione...
}
catch (IOException ex)
{
    Console.WriteLine("Errore di input/output: " + ex.Message);
}
catch (UnauthorizedAccessException ex)
{
    Console.WriteLine("Accesso al file negato: " + ex.Message);
}
catch (Exception ex)
{
    Console.WriteLine("Errore sconosciuto: " + ex);
}

Sì, tutto semplice — usiamo il familiare try-catch, ma nel metodo asincrono. È importante che il metodo stesso abbia la parola chiave async, altrimenti il compilatore si arrabbierà.

Dettaglio importante: dove mettere il await


Task<int> readTask = fs.ReadAsync(buffer, 0, buffer.Length);
// ... qui per sbaglio abbiamo dimenticato await o la gestione

In tal caso l'errore, se succede, finirà nel Task stesso (Task), e non lo saprai finché non cercherai di ottenere il risultato — per esempio con await readTask o usando la proprietà Task.Exception. Se ti dimentichi completamente del await — il Task può terminare con un errore e nessuno te lo dirà.

3. Perché il codice asincrono è insidioso senza gestione degli errori

Scenario 1: «Fire and forget» — la trappola del principiante


FileStream fs = new FileStream("file.txt", FileMode.Open);
byte[] buffer = new byte[8000];
fs.ReadAsync(buffer, 0, buffer.Length);
// E poi il programma continua per la sua strada

L'operazione di lettura va in «navigazione parallela», e se termina con un errore, nessun catch la prenderà. L'eccezione rimarrà nascosta dentro il Task. Questo pattern si chiama «fire and forget» e nelle applicazioni reali può causare perdita di errori critici e perdite di risorse.

Scenario 2: Metodi asincroni senza await


Task t = MyAsyncMethod();
// ... qui facciamo qualcosa e poi ci dimentichiamo di t

Gli errori avvenuti dentro MyAsyncMethod non saliranno in superficie finché non aspetti esplicitamente il Task (await t o t.Wait()).

4. Come gestire correttamente gli errori dai Task asincroni

Strategia 1: Usa sempre await


try
{
    await SomeFileOperationAsync();
}
catch (Exception ex)
{
    Console.WriteLine("Qualcosa è andato storto: " + ex.Message);
}

Così l'eccezione verrà lanciata proprio nel punto di attesa e non andrà persa.

Strategia 2: Gestione con .ContinueWith

Se per qualche motivo non usi await, puoi aggiungere un gestore errori tramite ContinueWith:


var task = fs.ReadAsync(buffer, 0, buffer.Length);
task.ContinueWith(t =>
{
    if (t.Exception != null)
        Console.WriteLine("Errore durante la lettura asincrona: " + t.Exception.InnerException);
}, TaskContinuationOptions.OnlyOnFaulted);

Onestamente? Nelle applicazioni C# moderne si usa raramente — async/await rende il codice più semplice e pulito.

Possibili eccezioni quando si lavora con file in modo asincrono

5. Esempio: Lettura asincrona con gestione degli errori

Aggiungiamo questa logica alla nostra applicazione:


using System;
using System.IO;
using System.Threading.Tasks;

namespace FileAsyncDemo
{
    class Program
    {
        static async Task Main()
        {
            string path = "bigfile.txt";
            byte[] buffer = new byte[4096];

            try
            {
                using FileStream fs = new FileStream(path, FileMode.Open);
                int bytesRead = await fs.ReadAsync(buffer, 0, buffer.Length);
                Console.WriteLine($"Letti {bytesRead} byte da {path}");
            }
            catch (FileNotFoundException ex)
            {
                Console.WriteLine("File non trovato: " + ex.Message);
            }
            catch (UnauthorizedAccessException ex)
            {
                Console.WriteLine("Accesso al file negato: " + ex.Message);
            }
            catch (IOException ex)
            {
                Console.WriteLine("Errore di lettura/scrittura: " + ex.Message);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Altro errore: " + ex.Message);
            }
        }
    }
}

Nota importante

Se non usi await in Main, ma fai solo Task result = SomeAsyncMethod(); — gli errori rimarranno «silenziosi» e si manifesteranno più tardi, quando proverai a ottenere il risultato.

6. Errori più comuni e «trappole» nella gestione degli errori

Dimenticare di mettere await su un metodo asincrono — le eccezioni non emergono in tempo, il programma si comporta in modo imprevedibile.

Non avvolgere le chiamate asincrone in try-catch — l'applicazione crolla al primo errore.

Gestire solo Exception, ignorando eccezioni specifiche come UnauthorizedAccessException o OperationCanceledException — in pratica perdi diagnostica utile.

Usare task «fire and forget» senza logging esplicito degli errori — le eccezioni si perdono dentro il Task.

Pensare che se il Task è terminato allora è tutto andato bene. Bisogna aspettare esplicitamente il risultato (await) e gestire le eccezioni.

2
Compito
C# SELF, livello 42, lezione 3
Bloccato
Gestione degli errori durante la lettura di un file
Gestione degli errori durante la lettura di un file
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION