1. Introduzione
Quando il programma diventa un po' più complesso di un semplice Hello, world!, ti trovi subito davanti al problema di dover gestire errori diversi in modo diverso. Immagina: lavori con file, rete, DB — e per ogni scenario di errore serve una reazione diversa. Per esempio, se manca un file puoi proporre all’utente di sceglierne un altro, mentre per un errore di rete — riprovare o mostrare un messaggio incoraggiante tipo "Controlla il cavo, magari il gatto l’ha rosicchiato di nuovo".
In C# per questo si usano più blocchi catch di seguito. Ogni blocco si specializza su un tipo di eccezione (e i suoi figli).
Struttura di catch multipli
try
{
// Qui — codice che può lanciare diverse eccezioni
}
catch (FileNotFoundException ex)
{
Console.WriteLine($"File non trovato: {ex.Message}");
}
catch (IOException ex) // prende tutti gli errori di I/O, se non è FileNotFoundException
{
Console.WriteLine($"Errore di input/output: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"Qualcosa è andato storto: {ex.Message}");
}
Importante! Il compilatore scorre i blocchi dall’alto verso il basso e sceglie il primo che corrisponde al tipo. Se viene preso un FileNotFoundException, non va oltre nella catena.
Illustrazione della "catena"
| Tipo di eccezione | Quale blocco la prende? |
|---|---|
| FileNotFoundException | Primo catch |
| IOException (altro) | Secondo catch |
| ArgumentException | Terzo (generico) catch |
Perché non mischiare?
La "trappola" più ampia — tipo catch (Exception) — mettila sempre per ultima, altrimenti "inghiottirà" tutte le eccezioni troppo presto e i catch più specifici non verranno mai eseguiti.
È come spegnere tutti i rilevatori di fumo in un edificio solo perché uno ha suonato per un toast bruciato: così i prossimi incendi non verranno rilevati dal sistema.
Esempio pratico
using System;
using System.IO;
class Program
{
static void Main()
{
try
{
double result = CalculateAverageAgeFromFile("users.txt");
Console.WriteLine($"Età media — {result}");
}
catch (FileNotFoundException ex)
{
Console.WriteLine("Errore: file non trovato. Controlla il percorso.");
}
catch (FormatException ex)
{
Console.WriteLine("Errore nei dati: impossibile leggere l’età.");
}
catch (Exception ex)
{
Console.WriteLine($"Altro errore: {ex.Message}");
}
}
static double CalculateAverageAgeFromFile(string filePath)
{
// (Implementazione: legge il file, fa il parsing delle età, calcola la media)
// ...
throw new NotImplementedException();
}
}
Qui separiamo chiaramente cosa fare se il file non c’è (FileNotFoundException), e cosa se i dati nel file sono "strani" (FormatException). Tutti gli altri casi li prende il terzo blocco "di riserva".
2. Filtri catch: per le sfumature
I blocchi catch multipli sono comodi, ma a volte non bastano. Capita che dentro lo stesso tipo di eccezione vuoi reagire in modo diverso, a seconda delle circostanze.
Per esempio, se la rete non va perché è finito internet — puoi provare a riconnetterti. Se invece il server non risponde — magari conviene mostrare un altro messaggio.
Qui entrano in gioco i filtri catch — una vera superfeature di C#, che ti permette di intercettare non solo per tipo, ma anche per una condizione aggiuntiva.
Sintassi del filtro when
catch (IOException ex) when (ex.Message.Contains("диска нет"))
{
Console.WriteLine("Ops! Sembra che tu abbia staccato la chiavetta USB.");
}
catch (IOException ex)
{
Console.WriteLine("Altro errore di input/output: " + ex.Message);
}
Qui il primo blocco prende solo quelle IOException il cui messaggio contiene la frase "диска нет" — tutto il resto va nel secondo blocco.
Applicazione nella vita reale
I filtri sono utilissimi con gli errori di rete, quando devi decidere cosa fare in base alle proprietà interne dell’eccezione: riprovare o solo segnalare l’errore.
Un altro esempio: immagina che nel metodo che fa il parsing di un file vuoi distinguere non solo un FormatException "generico", ma anche — se riguarda l’età (tipo se l’età è scritta come "abc" invece di un numero).
catch (FormatException ex) when (ex.Message.Contains("возраст"))
{
Console.WriteLine("Errore: impossibile leggere l’età. Controlla i dati.");
}
catch (FormatException ex)
{
Console.WriteLine("Errore di formato dei dati: " + ex.Message);
}
Filtri e performance
I filtri sono comodi, ma ricorda che la condizione del filtro viene valutata prima di entrare nel blocco catch, quindi se non è vera — il corpo del blocco non viene proprio eseguito.
Tra l’altro, se il filtro lancia un’eccezione di suo (tipo nel tuo when fai una divisione per zero) — quell’eccezione catch non la prenderà mai. Quindi occhio!
3. Combinare catch multipli e filtri
Immagina che nel tuo progetto devi gestire non solo il tipo di eccezione, ma anche il suo stato interno. Per esempio, lavorando con IOException vuoi comportamenti diversi se l’errore riguarda i permessi o lo spazio su disco.
try
{
File.AppendAllText("log.txt", "Nuova voce\n");
}
catch (IOException ex) when (ex.Message.Contains("Нет места"))
{
Console.WriteLine("Errore: Spazio su disco esaurito!");
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("Errore: non hai i permessi per scrivere il file. Avvia come amministratore.");
}
catch (IOException ex)
{
Console.WriteLine("Altro errore del disco: " + ex.Message);
}
Qui usiamo sia il filtro che la divisione per tipo di errore. Questa flessibilità è super utile quando sviluppi un’app complessa e vuoi reagire in modo informativo ai problemi più comuni.
4. Sfumature e "trappole" dei filtri e dei catch multipli
- Il filtro catch può usare la variabile dell’eccezione (ex), ma non può modificarla.
- Se lanci un’eccezione dentro when, non verrà "presa" dallo stesso catch — andrà su per lo stack come se il filtro non ci fosse.
- La logica del filtro diventa parte del contratto: il tuo collega deve sapere che non tutte le IOException verranno prese — solo se la condizione è vera.
- Se usi un solo catch per tutto il metodo, ma dentro c’è un filtro, c’è il rischio che alcune eccezioni "sfuggano". Se hai dubbi — usa blocchi separati e chiari.
- Nei progetti grandi puoi usare i filtri per logiche centralizzate, tipo loggare solo gli errori critici.
5. Scenari per colloqui e lavoro vero
Conoscere filtri e catch multipli non serve solo per fare bella figura o per il clean code. È una skill vera che ti possono chiedere ai colloqui, soprattutto se il ruolo riguarda la manutenzione di app grandi, complesse e distribuite.
Esempi di domande:
- Come gestire in C# diversi scenari di errore nella lettura di un file, in modo da mostrare messaggi diversi per ogni situazione (file mancante, disco occupato, dati corrotti)?
- Come evitare di "soffocare" un errore importante se hai messo un blocco generico catch (Exception)?
- A cosa serve il filtro catch? Può esserci un throw dentro?
GO TO FULL VERSION