Gerarchia delle Eccezioni in C#

Concetti Chiave
  • Nel .NET Framework, le eccezioni sono gestite attraverso una gerarchia di classi che ereditano da Exception.
  • La classe base per tutte le eccezioni è System.Exception, da cui derivano ApplicationException e SystemException.
  • Le eccezioni possono essere nidificate, creando una catena di eccezioni che fornisce informazioni dettagliate sugli errori.
  • È importante utilizzare le eccezioni di applicazione per gestire errori specifici dell'applicazione, mentre le eccezioni di sistema sono riservate al framework e agli errori critici.
  • Le eccezioni di sistema come StackOverflowException e OutOfMemoryException indicano errori gravi che spesso non possono essere recuperati, mentre le eccezioni di applicazione possono essere utilizzate per gestire errori specifici e prevedibili nel codice.

Gerarchia delle Eccezioni

Ci sono due tipi di eccezioni nel .NET Framework: eccezioni lanciate dalle applicazioni che sviluppiamo (ApplicationException) ed eccezioni lanciate dal runtime (SystemException).

Ognuna di queste è una classe base per una gerarchia di classi di eccezioni:

Gerarchia delle Eccezioni in C#
Figura 1: Gerarchia delle Eccezioni in C#

Poiché tutte queste classi hanno caratteristiche diverse, le esamineremo una per una.

La Classe Exception

Nel .NET Framework, Exception è la classe base per tutte le eccezioni.

Diverse classi ereditano direttamente da essa, incluse ApplicationException e SystemException. Queste due classi sono classi base per quasi tutte le eccezioni che si verificano durante l'esecuzione del programma.

La classe Exception contiene una copia dello stack di chiamata al momento in cui l'istanza dell'eccezione è stata creata. La classe ha anche un messaggio (solitamente) breve che descrive l'errore (riempito dal metodo che genera l'eccezione).

Ogni eccezione può avere un'eccezione nidificata anche talvolta chiamata eccezione interna o eccezione avvolta.

La capacità di nidificare un'eccezione all'interno di un'altra eccezione è molto utile in alcuni casi e consente alle eccezioni di essere collegate nella cosiddetta catena di eccezioni o exception chain. In questo modo, possiamo ottenere informazioni più dettagliate su cosa è andato storto nel programma.

La Classe System.Exception

Ecco come appare la classe System.Exception:

[SerializableAttribute]
[ComVisibleAttribute(true)]
[ClassInterfaceAttribute(ClassInterfaceType.None)]
public class Exception : ISerializable, _Exception
{
    public Exception();
    public Exception(string message);
    public Exception(string message, Exception innerException);
    public virtual IDictionary Data { get; }
    public virtual string HelpLink { get; set; }
    protected int HResult { get; set; }
    public Exception InnerException { get; }
    public virtual string Message { get; }
    public virtual string Source { get; set; }
    public virtual string StackTrace { get; }
    public MethodBase TargetSite { get; }
    public virtual Exception GetBaseException();
}

La specifica completa della classe Exception fornita sopra è complessa da spiegare, quindi discuteremo solo i suoi metodi e proprietà più importanti poiché vengono ereditati da tutte le eccezioni nel .NET Framework.

  • Abbiamo tre costruttori con diverse combinazioni per messaggio ed eccezione interna:

    • Il primo costruttore crea un'istanza di Exception senza messaggio o eccezione interna.
    • Il secondo accetta un messaggio di errore come parametro.
    • Il terzo accetta sia un messaggio di errore che un'eccezione interna.
  • La proprietà Message restituisce una descrizione testuale dell'eccezione.

    Ad esempio, se l'eccezione è FileNotFoundException, il messaggio potrebbe fornire informazioni su quale file non è stato trovato. Nella maggior parte dei casi, il codice che genera l'eccezione passa il messaggio nel costruttore. Una volta impostata, la proprietà Message non può essere modificata.

  • La proprietà InnerException restituisce l'eccezione interna (avvolta, annidata) o null se tale eccezione non esiste.

  • Il metodo GetBaseException() restituisce l'eccezione più interna da una catena di eccezioni data.

    Per definizione, chiamare questo metodo per ogni eccezione all'interno di una catena di eccezioni produrrà sempre lo stesso risultato – la prima eccezione che si è verificata.

  • La proprietà StackTrace restituisce informazioni per l'intero stack contenuto nell'eccezione (abbiamo già visto come appare questa informazione).

Eccezioni di Sistema e di Applicazione

Le eccezioni in .NET sono di due tipi: di sistema e di applicazione.

Le eccezioni di sistema sono definite nelle librerie .NET e sono utilizzate dal framework, mentre le eccezioni di applicazione sono definite dagli sviluppatori di applicazioni e sono utilizzate dal software applicativo.

Quando noi, come sviluppatori, progettiamo le nostre classi di eccezione, è una buona pratica ereditare da ApplicationException e non direttamente da SystemException (o anche peggio, direttamente da Exception). SystemException dovrebbe essere ereditata solo internamente all'interno del .NET Framework.

Alcune delle peggiori eccezioni di sistema includono ExecutionEngineException (che viene lanciata su errore interno all'interno del CLR), StackOverflowException (overflow dello stack di chiamata, molto probabilmente dovuto a ricorsione infinita) e OutOfMemoryException (memoria insufficiente). In tutti questi casi, la nostra applicazione difficilmente potrebbe recuperare o reagire in qualche modo ragionevole. Più frequentemente, quando tale eccezione si verifica, l'applicazione semplicemente si blocca.

Le eccezioni relative all'interazione con componenti esterni (come i componenti COM) ereditano da ExternalException. Esempi sono COMException e Win32Exception.