back to top

Le eccezioni: gestire gli errori in C#

Le applicazioni sono soggette ad errori e quindi quando si sviluppa un’applicazione è necessario svolgere un’attenta attività di testing ma soprattutto scrivere codice che gestisca opportunamente tali situazioni. Gestire le eccezioni è fondamentale in C# perché consente di rintracciare e gestire qualunque errore possa verificarsi. Una corretta gestione delle eccezioni, quindi, è necessaria affinché i nostri programmi funzionino nel miglior modo possibile anche in presenza di situazioni "anomale", cioè tutte le volte in cui si verifca un risultato diverso da quello auspicabile.

Gli errori più comuni in C#

Gli errori possono verificarsi in varie situazioni e quelli più comuni riguardano, ad esempio, i tentativi di divisione per zero ed i tentativi di connessione a risorse come file che non esistono o database per i quali non vengono specificate credenziali di accesso valide. Un’eccezione diffusissima è la null reference exception, che solitamente si verifica quando un programma cerca di utilizzare un oggetto non inizializzato.

Il costrutto try-catch-finally

Quando un programma C# viene eseguito , così come ogni programma realizzato con qualsiasi altro linguaggio, possono verificarsi situazioni impreviste. Per gestire tali situazioni si utilizza la sintassi try – catch – finally grazie alla quale, appunto, andremo a gestire gli eventuali errori all’interno del nostro codice, utilizzando dei blocchi ben definiti.

Vediamo la sintassi di questo costrutto:

try
{
  // Codice che effettua le varie operazioni
}
catch
{
  // Rilevazione e gestione di un errore
}
finally
{
  // Operazioni da effettuare in ogni caso
}

L’istruzione try abilita la gestione degli errori ed ogni eccezione che si verifica al suo interno viene catturata automaticamente determinando l’esecuzione del codice contenuto nel blocco catch. Indipendentemente dalla presenza di errori o meno il codice contenuto nel blocco finally viene sempre eseguito e questo è molto utile per la gestione di operazioni di "pulizia" (come, ad esempio, la chiusura di connessioni al database). Si noti che il blocco finally è facoltativo (e, quindi, può essere omesso).

Vediamo un esempio di implementazione di una semplice struttura try-catch:

using System;
class Esempio
{
  public static void Main()
  {
    int n1 = 100;
    int n2 = 0;
    try {
      Console.WriteLine(n1 / n2);
    }
    catch {
      System.Console.WriteLine("La divisione per 0 non va bene!");
    }
  }
}

Se provate ad eseguire questo codice potrete notare come venga mostrato a video un messaggio di errore.

Come risulta evidente dal nostro esempio, non abbiamo fatto altro che inserire all’interno del blocco try le istruzioni del nostro programma e che potrebbero essere fonte di un errore (nel nostro esempio l’errore non è una eventualità ma una certezza). Nel blocco catch, invece, abbiamo inserito il codice da eseguire nel momento in cui l’errore si verifica (nell’esempio stampiamo a video un avviso esplicativo sull’accaduto).

Si noti che nel nostro esempio abbiamo usato un catch generico ma avremmo potuto costruire anche condizioni più specifiche, ad esempio avremmo potuto creare un blocco apposito per un certo tipo di errore. Ad esempio:

using System;
class Esempio
{
  public static void Main()
  {
    int n1 = 100;
    int n2 = 0;
    try {
      Console.WriteLine(n1 / n2);
    }
    catch (System.DivideByZeroException) {
      System.Console.WriteLine("La divisione per 0 non va bene!");
    }
  }
}

In quest’ultimo esempio il blocco catch non intercetterà tutti gli errori ma solo quelli del tipo DivideByZero. Per farlo abbiamo fatto ricorso ad una classe derivata da System.Exception.

La gestione strutturata delle eccezioni

Quando si manifesta un errore nel codice, il .NET Framework verifica se siano presenti o meno gestori di eventi nel contesto in cui esso si verifica. Se non vengono individuati gestori l’applicazione viene terminata. Ma un’applicazione dovrebbe essere in grado di identificare l’errore e, ove possibile, porvi rimedio.

Per ottenere questo risultato il linguaggio C# supporta la cosiddetta gestione strutturata degli errori. Questo significa che quando si verifica un errore in un’applicazione il .NET Framework crea un oggetto che rappresenta la problematica riscontrata ed è possibile ottenere ed analizzare tale oggetto utilizzando un gestore di eccezioni (exception handler).

Tutte le classi relative ad eccezioni derivano dalla classe base System.Exception. Il .NET Framework fornisce diverse classi di eccezioni (NullReferenceException, DivideByZeroException, IOException, SqlException, ecc.) e la classe Exception include le funzionalità essenziali per identificare qualsiasi tipo d’errore.

Nell’esempio precedente abbiamo già visto un esempio quando abbiamo fatto ricorso alla classe DivideByZeroException.

La gestione strutturata delle eccezioni è molto flessibile poichè permette di individuare specifici tipi di eccezioni. Per fare questo è necessario inserire più istruzioni catch in sequenza ognuna delle quali deputata ad individuare un particolare tipo di eccezione. Vediamo un esempio:

try
{

}
catch (System.Data.SqlClient.SqlException err)
{
  // Eccezioni di tipo SqlException
}
catch (System.NullReferenceException err)
{
  // Eccezioni di tipo NullReferenceException
}
catch (System.Exception err)
{
  // Eccezioni generiche di altro tipo
}

I tipi di eccezione in questo modello di gestione devono essere disposti dal più specifico al più generico. Nel codice di esempio appena visto quando manifesta un errore per prima cosa si verifica se l’errore è di tipo sql, poi si verifica se è legato ad un valore nullo e infine viene posto il blocco con la gestione di un’eccezione generica che verrà sempre evidenziata poichè, come detto, ogni oggetto di tipo eccezione è derivato dalla classe base System.Exception.

L’istruzione throw

Attraverso l’istruzione throw lo sviluppatore può generare, in modo esplicito, delle eccezioni all’interno di un programma C#. Più tecnicamente parlando, throw è un’istanza della classe Exception grazie alla quale è possibile, a livello applicativo, "lanciare" un eccezione in modo arbitrario.

Pubblicità
Articolo precedente
Articolo successivo