La gestione delle eccezioni in C Sharp

Giorgio Borelli

Come gestire l'eccezioni nel codice C#, con le istruzioni try, catch, finally e throwLa gestione delle eccezioni indipendentemente dal linguaggio usato è un'aspetto fondamentale di ogni buon programma, sia questo un'applicazione web che un programma stand alone.

Per quanto ben progettato, nessun programma è esente da bug, sia il contesto sia gli errori imprevisti possono comportarne un malfunzionamento o peggio un crash, e quest'ultima è proprio l'effetto che vogliamo evitare assolutamente.

Tramite la gestione delle eccezioni è possibile intercettare e gestire in modo opportuno tutti quegli errori, eventi imprevisti e situazioni anomali, consentendo al programma di ripristinarsi ad uno stato normale ed all'utente di riprenderne il normale uso. Un uso accorto della gestione delle eccezioni permette pertanto di scrivere programmi più robusti ed efficaci.

Tratteremo in questo articolo l'uso dei blocchi, delle parole chiavi, del lancio e della personalizzazione delle eccezioni in C#.

In C# un'eccezione è quindi un'oggetto creato a partire dalla classe System.Exception, che contiene tutta una serie d'informazioni sull'errore verificatosi, questo però rappresenta l'eccezione più generica presente nell'ambito del .NET framework, è possibile anche definire eccezioni particolareggiate, create da classi più specializzate per il contesto in cui devono essere usate, come ad es. IOException per le operazioni d'INPUT OUTPUT o DivideByZeroException per la divisione per zero.

Al verificarsi di una eccezione questa comincia a risalire lo stack delle chiamate sino ad intercettare un gestore in grado di prenderla in carico e gestirla, in quel momento il flusso dell'elaborazione e della corretta sequenza delle chiamate viene interrotto, cedendo il controllo al gestore dell'eccezione, non appena questo termina l'elaborazione riprende dalla chiamata immediatamente successiva a quella del gestore. Questo meccanismo fa sì che eccezioni più "interne" al codice possano essere gestite a livello più alto tramite ad es. un gestore generico.

I blocchi per definire la gestione delle eccezioni sono: try, catch e finally. Tramite la parola chiave try e le parentesi graffe definiamo un blocco di codice nel quale è possibile intercettare l'eccezioni, nel qual caso le istruzioni presenti all'interno del blocco di codice comprese chiamate a funzioni generano un'eccezione, questa viene intercettata e passata al gestore, che si definisce con la parola chiave catch. All'interno del catch andremo a posizionare le istruzioni necessarie per la gestione dell'eccezione e per la visualizzazione di un messaggio relativo all'errore. Invece con la parola chiave finally si definisce il blocco di codice che chiude la gestione dell'eccezione, il suo uso è opzionale ma deve essere obbligatoriamente presente se non viene usato il blocco catch; indipendentemente dal verificarsi o meno di un'eccezione e della presenza del blocco catch o no, l'elaborazione del codice (se definito) deve passare obbligatoriamente per il blocco finally, ed è solitamente usato per effettuare dei controlli e rilasciare l'eventuali risorse impegnate.

try
{
    //istruzioni che possono generare l'eccezione
}
catch (Exception exc1)
{
    //gestione generica dell'eccezione
}
catch (FileNotFoundException exc2)
{
    //gestione specifica dell'eccezione
}
finally
{
    //chiude il try/catch liberando solitamente le risorse
}

Notate subito che abbiamo specificato non uno, ma bensì due blocchi catch, infatti e possibile definire tutti i catch voluti, ed ognuno di essi può gestirà l'eccezione più specifica per la sua definizione, se ad es. nel blocco try si verifica un'eccezione causata dal riferimento in memoria ad un oggetto nullo questa sarà gestita dal catch con Exception poichè rappresenta l'eccezione più generica che si possa scatenare e non è presente il gestore specifico NullReferenceException per questo tipo d'eccezione. Se l'eccezione riguardasse invece il tentativo di aprire un file inesistente allora il controllo verrebbe passato direttamente al secondo catch ovvero al FileNotFoundException poichè più specifico rispetto al primo. Anche l'ordine con cui gestire l'eccezioni tramite i catch è importante, è bene infatti specificare per primi i gestori più specifici e via via quelli più generici, nell'esempio precedente infatti il compilatore potrebbe avvisarci che esiste già un gestore più generico in grado d'intercettare l'eccezione definita dopo, in questo caso FileNotFoundException.

E' possibile anche generare un'eccezione (generica e/o specifica) volutamente tramite la parola chiave throw, in questo caso l'eccezione lanciata risale lo stack delle chiamate sino ad incontrare il primo gestore adatto che la prende in carico.

...
throw new ArgumentOutOfRangeException("Messaggio d'errore");
...

Nell'eccezione lanciata è possibile specificare un messaggio d'errore personalizzato che sarà visualizzato nella proprietà Message dell'eccezione catturata. Se non dovesse essere presente nessun gestore d'eccezione, risalendo risalendo lo stack, alla fine verrà presentato il messaggio di "Eccezione non gestita".

Infine è possibile definire le proprie eccezioni personalizzate, usabili per ottenere il massimo della specializzazione e personalizzazione dei messaggi inerenti la gestione degli errori della propria applicazione. E' possibile definire la propria classe d'eccezione, facendola derivare o dalla classe generica System.Exception o da una classe per l'eccezioni più specifiche, come nell'esempio seguente, nel quale definiamo una classe eccezione che deriva dalla classe ApplicationException

class MyPersonalException: ApplicationException
{
    public MyPersonalException():base() { }
    public MyPersonalException(string error):base(error) { }
}

è possibile definire i costruttori della propria classe per presentare messaggi personalizzati d'errore, arricchirle anche con metodi e proprietà per personalizzarle secondo l'esigenze più specifiche di ogni programmatore nella gestione dell'eccezioni del proprio programma.

 

Conclusioni sull'eccezioni in C#

Desidero concludere questa spiegazione sulla gestione dell'eccezioni con un'osservazione, scrivere programmi robusti in grado gestire qualsiasi situazione anomala senza crashare è sicuramente un requisito importante, e bene però ricordare che la gestione dell'eccezione ha il suo costo in termini di risorse impegnate, la gestione di un'eccezione con l'interruzione del normale flusso d'elaborazione ha un costo non indifferente in termini di performance dell'applicazione, è pertanto giusto farne un uso adeguato, evitando ove possibile il generarsi dell'eccezione stessa, ovvero prevenire e meglio che curare, e bene valutare che un controllo ii più che possa evitare il verificarsi di una possibile eccezione e conseguentemente la sua gestione è da preferire rispetto alla gestione dell'eccezione stesse.

Chiunque voglia aggiungere le proprie considerazioni sull'uso e gestione dell'eccezione o approfondire l'argomento può farlo tramite i commenti.

Categorie: C#

Tags:

Aggiungi Commento

biuquote
Loading