Esaminare le eccezioni e il processo di gestione delle eccezioni
- 11 minuti
Gli errori di runtime in un'applicazione C# vengono gestiti usando un meccanismo denominato eccezioni. Le eccezioni offrono un modo strutturato, uniforme e indipendente dai tipi di gestione delle condizioni di errore a livello di sistema e a livello di applicazione. Le eccezioni vengono generate dal runtime .NET o dal codice in un'applicazione.
Scenari comuni che richiedono la gestione delle eccezioni
Esistono diversi scenari di programmazione che richiedono la gestione delle eccezioni. Molti di questi scenari comportano una qualche forma di acquisizione dei dati. Anche se alcuni degli scenari prevedono tecniche di codifica che non rientrano nell'ambito di questo training, vale comunque la pena notare.
Gli scenari comuni che richiedono la gestione delle eccezioni includono:
Input utente: le eccezioni possono verificarsi quando il codice elabora l'input dell'utente. Ad esempio, le eccezioni si verificano quando il valore di input è in formato errato o non compreso nell'intervallo.
Elaborazione e calcoli dei dati: le eccezioni possono verificarsi quando il codice esegue calcoli o conversioni di dati. Ad esempio, le eccezioni si verificano quando il codice tenta di dividere per zero, eseguire il cast a un tipo non supportato o assegnare un valore non compreso nell'intervallo.
Operazioni di input/output dei file: le eccezioni possono verificarsi quando il codice legge o scrive in un file. Ad esempio, le eccezioni si verificano quando il file non esiste, il programma non dispone dell'autorizzazione per accedere al file o il file è in uso da un altro processo.
Operazioni di database: le eccezioni possono verificarsi quando il codice interagisce con un database. Ad esempio, le eccezioni si verificano quando la connessione al database viene persa, si verifica un errore di sintassi in un'istruzione SQL o si verifica una violazione del vincolo.
Comunicazione di rete: le eccezioni possono verificarsi quando il codice comunica in rete. Ad esempio, le eccezioni si verificano quando la connessione di rete viene persa, si verifica un timeout o il server remoto restituisce un errore.
Altre risorse esterne: le eccezioni possono verificarsi quando il codice comunica con altre risorse esterne. I servizi Web, le API REST o le librerie di terze parti possono generare eccezioni per vari motivi. Ad esempio, le eccezioni si verificano a causa di problemi di connessioni di rete, dati in formato non valido e così via.
Parole chiave di gestione delle eccezioni, blocchi di codice e modelli
La gestione delle eccezioni in C# viene implementata usando le tryparole chiave , catche finally . Ognuna di queste parole chiave ha un blocco di codice associato e può essere usata per soddisfare un obiettivo specifico nell'approccio alla gestione delle eccezioni. Per esempio:
try
{
// try code block - code that may generate an exception
}
catch
{
// catch code block - code to handle an exception
}
finally
{
// finally code block - code to clean up resources
}
Annotazioni
Il linguaggio C# consente anche al codice di generare un oggetto eccezione usando la throw parola chiave . Gli scenari di gestione delle eccezioni che includono l'uso della throw parola chiave per generare eccezioni sono trattati in un modulo separato in Microsoft Learn.
Il blocco di codice try contiene il codice protetto che potrebbe generare un'eccezione. Se il codice all'interno di un try blocco causa un'eccezione, l'eccezione viene gestita da un blocco corrispondente catch .
Il blocco di codice catch contiene il codice eseguito quando viene intercettata un'eccezione. Il catch blocco può gestire l'eccezione, registrarla o ignorarla. Un catch blocco può essere configurato per l'esecuzione quando si verifica un tipo di eccezione o solo quando si verifica un tipo specifico di eccezione.
Il finally blocco di codice contiene codice che esegue se si verifica o meno un'eccezione. Il finally blocco viene spesso usato per pulire tutte le risorse allocate in un try blocco. Ad esempio, verificando che a una variabile sia assegnato il valore corretto o obbligatorio.
La gestione delle eccezioni in un'applicazione C# viene in genere implementata usando uno o più dei modelli seguenti:
- Il
try-catchcriterio è costituito da untryblocco seguito da una o piùcatchclausole. Ognicatchblocco viene usato per specificare i gestori per eccezioni diverse. - Il
try-finallymodello è costituito da untryblocco seguito da unfinallyblocco. In genere, le istruzioni di unfinallyblocco vengono eseguite quando il controllo lascia un'istruzionetry. - Il
try-catch-finallymodello implementa tutti e tre i tipi di blocchi di gestione delle eccezioni. Uno scenario comune per iltry-catch-finallymodello è quando le risorse vengono ottenute e usate in untryblocco, le circostanze eccezionali vengono gestite in uncatchblocco e le risorse vengono rilasciate o altrimenti gestite nelfinallyblocco.
In che modo le eccezioni sono rappresentate nel codice?
Le eccezioni sono rappresentate nel codice come oggetti, ovvero sono un'istanza di una classe. La libreria di classi .NET fornisce classi di eccezioni a cui si accede nel codice esattamente come altre classi .NET. Un altro esempio di classe .NET usata come oggetto nel codice è la Random classe (usata per creare numeri casuali).
Più precisamente, le eccezioni sono tipi, rappresentati da classi che sono tutte derivate da System.Exception. Una classe di eccezione derivata da Exception include informazioni che identificano il tipo di eccezione e contengono proprietà che forniscono informazioni dettagliate sull'eccezione. Un esame più dettagliato della Exception classe è incluso più avanti in questo modulo.
Un'istanza di runtime di una classe viene in genere definita oggetto , quindi le eccezioni vengono spesso definite oggetti eccezione.
Annotazioni
Anche se talvolta vengono usati in modo intercambiabile, una classe e un oggetto sono cose diverse. Una classe definisce un tipo di oggetto, ma non è un oggetto stesso. Un oggetto è un'entità concreta basata su una classe .
Processo di gestione delle eccezioni
Quando si verifica un'eccezione, il runtime .NET cerca la clausola più catch vicina in grado di gestire l'eccezione. Il processo inizia con il metodo che ha causato la generazione dell'eccezione. In primo luogo, il metodo viene esaminato per verificare se il codice che ha causato l'eccezione si trova all'interno di un try blocco di codice. Se il codice si trova all'interno try del blocco di codice, le catch clausole associate all'istruzione try vengono considerate in ordine. Se le catch clausole non sono in grado di gestire l'eccezione, viene eseguita la ricerca del metodo che ha chiamato il metodo corrente. Questo metodo viene esaminato per determinare se la chiamata al metodo (al primo metodo) si trova all'interno di un try blocco di codice. Se la chiamata si trova all'interno di un try blocco di codice, le clausole associate catch vengono considerate. Questo processo di ricerca continua fino a quando non viene trovata una catch clausola in grado di gestire l'eccezione corrente.
Dopo aver trovato una catch clausola in grado di gestire l'eccezione, il runtime prepara il trasferimento del controllo alla prima istruzione del catch blocco. Tuttavia, prima dell'avvio dell'esecuzione del catch blocco, il runtime esegue tutti i finally blocchi associati alle try istruzioni trovate durante la ricerca. Se vengono trovati più finally blocchi, vengono eseguiti in ordine, a partire da quello più vicino al codice che ha causato la generazione dell'eccezione.
Se non viene trovata alcuna catch clausola per gestire l'eccezione, il runtime termina l'applicazione e visualizza un messaggio di errore all'utente.
Si consideri l'esempio di codice seguente che include un try-finally modello annidato all'interno di un try-catch modello:
try
{
// Step 1: code execution begins
try
{
// Step 2: an exception occurs here
}
finally
{
// Step 4: the system executes the finally code block associated with the try statement where the exception occurred
}
}
catch // Step 3: the system finds a catch clause that can handle the exception
{
// Step 5: the system transfers control to the first line of the catch code block
}
In questo esempio viene eseguito il processo seguente:
- L'esecuzione inizia nel blocco di codice dell'istruzione esterna
try. - Viene generata un'eccezione nel blocco di codice dell'istruzione interna
try. - Il runtime trova la
catchclausola associata all'istruzione esternatry. - Prima che il runtime trasferisca il controllo alla prima riga del
catchblocco di codice, esegue lafinallyclausola associata all'istruzione internatry. - Il runtime trasferisce quindi il controllo alla prima riga del
catchblocco di codice ed esegue il codice che gestisce l'eccezione.
In questo semplice esempio, i modelli e annidati try-catch si trovano all'interno di un singolo metodo, ma più try-catch modelli e try-finally possono essere distribuiti tra metodi che chiamano altri metodi.try-finally
Gestione delle eccezioni e stack di chiamate
Il termine "rimozione dello stack di chiamate" viene spesso visualizzato quando si legge la gestione delle eccezioni e il processo di gestione delle eccezioni. Per comprendere questo termine, è necessario comprendere lo stack di chiamate e come viene usato per tenere traccia dello "stack" delle chiamate al metodo durante l'esecuzione del codice.
Si può pensare allo stack di chiamate come una torre di blocchi. Quando si costruisce una torre, si inizia con un solo blocco. Ogni volta che si aggiunge un blocco alla torre, lo si posiziona sopra i blocchi esistenti. Quando l'applicazione viene avviata nell'esecuzione nel debugger, il punto di ingresso all'applicazione è il primo livello aggiunto allo stack di chiamate (il primo blocco della torre). Ogni volta che un metodo chiama un altro metodo, il nuovo metodo viene aggiunto all'inizio dello stack. Quando il codice esce da un metodo, il metodo viene rimosso dallo stack di chiamate.
Annotazioni
Per un'applicazione console, il punto di ingresso dell'applicazione è costituito dalle istruzioni di primo livello. Nello stack di chiamate di Visual Studio Code questo punto di ingresso viene definito Main metodo .
La rimozione dello stack di chiamate è il processo usato dal runtime .NET quando si verifica un errore in un programma C#. È lo stesso processo appena esaminato.
Tornando all'analogia della torre del blocco, quando è necessario rimuovere un blocco dalla torre, si inizia dalla parte superiore e si rimuove ogni blocco fino a raggiungere quello necessario. Questo processo è simile al funzionamento della rimozione dello stack di chiamate, in cui ogni livello di chiamata nello stack è simile a un blocco nella torre. Quando il runtime deve rimuovere lo stack di chiamate, inizia dalla parte superiore e rimuove ogni livello di chiamata fino a raggiungere quello di cui ha bisogno. In questo caso, il livello di chiamata necessario è il metodo con una catch clausola che può gestire l'eccezione che si è verificata.
Riepilogo
Ecco i concetti più importanti di questa unità da ricordare:
- Gli scenari comuni che possono richiedere la gestione delle eccezioni includono l'input dell'utente, l'elaborazione dei dati, le operazioni di I/O dei file, le operazioni di database e la comunicazione di rete.
- La gestione delle eccezioni in C# viene implementata usando
tryle parole chiave ,catchefinally. Ogni parola chiave ha un blocco di codice associato che svolge uno scopo specifico. - Le eccezioni sono rappresentate come tipi e derivate dalla
System.Exceptionclasse in .NET. Le eccezioni contengono informazioni che identificano il tipo di eccezione e le proprietà che forniscono dettagli aggiuntivi. - Quando si verifica un'eccezione, il runtime .NET cerca la clausola più
catchvicina che può gestirla. La ricerca inizia con il metodo in cui è stata generata l'eccezione e sposta lo stack di chiamate verso il basso, se necessario.