Pagina personale di:
Carlo Vecchio
appunti di C#, R, SQL Server, ASP.NET, algoritmi, numeri
Vai ai contenuti

C# - La gestione delle eccezioni

C#

Le parole chiave 'try', 'catch', 'finally' e 'throw'

  • Una Eccezione (Exception) è un errore del programma non previsto.
  • È consuetudine gestire le eccezioni per far sì che all'utente non appaiano messaggi criptici. Per gestire le eccezioni, la classe System.Exception fornisce diverse proprietà, tra cui la proprietà 'Message' che è una descrizione dell'eccezione.
  • Per gestire le eccezioni, occorre utilizzare le parole chiave ‘try’, ‘catch’ e opzionalmente ‘finally’:
  • Ecco come utilizzarle:

   try
   {
       // Codice nel quale è gestita l’eccezione.
       ...
   }
   catch
   {
       // Codice da eseguire in caso di eccezione.
       ...
   }


  • Lo statement ‘catch’ può ricevere come parametro una variabile di tipo System.Exception, in questo modo si può capire la causa dell’eccezione:

   try
   {
       // Codice nel quale è gestita l’eccezione.
       ...
   }
   catch (Exception ex)
   {
      // Codice da eseguire in caso di eccezione.
      Console.WriteLine("È avvenuta la seguente eccezione: {0}", ex.Message);
      // Proprietà interessanti nella gestione dell'eccezione.
      Console.WriteLine("Message: " + ex.Message);
      Console.WriteLine("Source: " + ex.Source);
      Console.WriteLine("StackTrace: " + ex.StackTrace);
   }


  • È possibile catturare delle eccezioni specifiche nel modo seguente.
  • Le eccezioni vengono valutate nell’ordine in cui sono scritte.
  • Viene eseguito il codice solo della prima eccezione valutata positivamente.

   try
   {
       // codice nel quale è gestita l’eccezione
       ...
   }
   catch (System.IndexOutOfRangeException ex)
   {
       // codice da eseguire in caso di eccezione specifica
       ...
   }
   catch (System.Exception ex)
   {
       // codice da eseguire in caso di eccezione
       Console.WriteLine("È avvenuta la seguente eccezione: {0}", ex.Message);
   }


  • Per eseguire del codice indipendentemente dal fatto che sia stata generata una eccezione, utilizzare lo statement ‘finally’. È una buona pratica rilasciare le risorse in questo blocco (per esempio chiudere file, pulire la memoria, ...).


   try
   {
       // Codice nel quale è gestita l’eccezione.
       ...
   }
   catch (Exception ex)
   {
       // Codice da eseguire in caso di eccezione.
      ...
   }
   finally
   {
       // Codice che viene eseguito sempre.
       ...
   }


  • Per causare una eccezione, utilizzare lo statement ‘throw’ seguito dall’eccezione che si vuole causare.
  • L'eccezione seguente ha nella proprietà 'Message' il messaggio di default.

   throw new System.IndexOutOfRangeException();

  • È possibile personalizzare la proprietà 'Message' utilizzando uno degli overload a disposizione, per esempio:

   throw new System.IndexOutOfRangeException("Controllare il programma, c'è un indice fuori range!");

  • Per lanciare l'eccezione al metodo chiamante, basta l'istruzione ‘throw’ senza altre indicazioni. Nel metodo chiamante ovviamente va gestita l'eccezione.

   private void Funzione1()
   {
       try
       {
           ...
       }
       catch
       {
           throw;
       }
   }


Inner Exception

  • La proprietà InnerException, ritorna l'istanza dell'eccezione che ha causato l'eccezione corrente. Questa proprietà è utilizzata per ottenere la sequenza di eccezioni in presenza di programmi ben strutturati, dove ci sono metodi che richiamano altri metodi.
  • Per esempio nel seguente codice, il programma esegue ad un certo punto il metodo 'Funzione1()'. Questo metodo genera una eccezione che viene passata al metodo chiamante tramite un 'throw'. Un overload del 'throw' permette di passare la descrizione dell'eccezione e un riferimento all'eccezione stessa. Il metodo chiamante ha quindi a disposizione l'eccezione generata più l'eccezione originaria.

   private void Form1_Load(object sender, EventArgs e)
   {
       try
       {
           // Altre istruzioni ...
           Funzione1();
           // Altre istruzioni ...
       }
       catch (Exception ex)
       {
           Console.WriteLine("Message: " + ex.Message);
           if (ex.InnerException != null)
           {
               Console.WriteLine("Message: " + ex.InnerException.Message);
           }
       }
   }

   private void Funzione1()
   {
       try
       {
           int i = 0;
           i = 10 / i;
       }
       catch (Exception ex)
       {
           throw new Exception("Errore nella Funzione1", ex);
       }
   }


  • È necessario sempre verificare che la proprietà 'InnerException' sia diversa da null. Infatti, con riferimento a questo esempio, se l'eccezione non fosse causata dal metodo 'Funzione1()', la suddetta proprietà sarebbe null.
  • L'esempio precedente può essere generalizzato quando si è in presenza di più metodi che chiamano altri metodi con molti livelli di profondità. Per esempio un metodo principale che chiama 'Funzione1()' che chiama 'Funzione2()' eccetera.
  • Se per ogni metodo si utilizza il 'throw' come nell'esempio precedente, nel metodo principale si hanno a disposizione tutte le eccezioni. Per leggerle basta allora un semplice ciclo 'while':

   try
   {
       // Altre istruzioni ...
       Funzione1();
       // Altre istruzioni ...
   }
   catch (Exception ex)
   {
       Console.WriteLine("Message: " + ex.Message);
       while (ex.InnerException != null)
       {
           Console.WriteLine("Message: " + ex.InnerException.Message);
           ex = ex.InnerException;
       }
   }


Definire nuove eccezioni (Custom Exceptions)

  • Si possono definire anche nuove eccezioni quando quelle disponibili non soddisfano.
  • Le nuove eccezioni sono classi che ereditano dalla classe Exception.
  • Per avere la stessa flessibilità che si ha nelle eccezioni del .NET, è buona regola definire tutti i costruttori possibili nella classe personalizzata. Questa flessibilità permette per esempio di gestire messaggi personalizzati (proprietà 'Message') e di recuperare l'eccezione originaria (proprietà 'InnerException').
  • I costruttori da definire sono quattro, come nell'esempio successivo.

   // Necessita di using System.Runtime.Serialization;
   [Serializable]
   public class MyException : Exception
   {
       public MyException()
           : base()
       { }

       public MyException(string message)
           : base(message)
       { }

       public MyException(string message, Exception innerException)
           : base(message, innerException)
       { }

       public MyException(SerializationInfo info, StreamingContext context)
           : base(info, context)
       { }
   }

  • L'ultimo costruttore permette all'eccezione di lavorare tra domini e l'oggetto deve essere serializzabile. Perciò bisogna marcare la classe con '[Serializable]' e aggiungere lo spazio dei nomi indicato nel commento del codice.

© 2020 Carlo Vecchio
Torna ai contenuti