Istruzioni per la generazione e la gestione di errori

Le istruzioni per la generazione e la gestione di errori sono delineate dalle regole riportate di seguito.

  • È necessario che tutti i percorsi di codice che generano un'eccezione forniscano un metodo che consenta di controllarne l'esito positivo senza generare un'eccezione. È possibile ad esempio chiamare il metodo File.Exists per evitare che venga generata un'eccezione FileNotFoundException. Sebbene in alcuni casi potrebbe non essere possibile, lo scopo è di evitare che vengano generate eccezioni durante la normale esecuzione.

  • Aggiungere ai nomi della classe Exception il suffisso Exception ** come nell'esempio di codice riportato di seguito.

    Public Class FileNotFoundException 
       Inherits Exception 
       ' Implementation code goes here.
    End Class
    [C#]
    public class FileNotFoundException : Exception 
    {
       // Implementation code goes here.
    }
    
  • In occasione della creazione di classi di eccezioni, utilizzare i costruttori comuni riportati nell'esempio di codice fornito di seguito.

    Public Class XxxException
       Inherits ApplicationException 
    
       Public Sub New()
          ' Implementation code goes here.
       End Sub
    
       Public Sub New(message As String)
          ' Implementation code goes here.
       End Sub
    
       Public Sub New(message As String, inner As Exception)
          ' Implementation code goes here.
       End Sub
    
       Public Sub New(info As SerializationInfo, context As StreamingContext)
          ' Implementation code goes here.
       End Sub
    End Class
    [C#]
    public class XxxException : ApplicationException 
    {
       public XxxException() {... }
       public XxxException(string message) {... }
       public XxxException(string message, Exception inner) {... }
       public XxxException(SerializationInfo info, StreamingContext context) {...}
    }
    
  • Nella maggior parte dei casi utilizzare i tipi di eccezione predefiniti. Definire nuovi tipi di eccezione solo per gli scenari di programmazione, in cui si prevede che gli utenti della libreria di classi intercettino le eccezioni del nuovo tipo ed eseguano un'azione a livello di codice in base al tipo di eccezione. Questa operazione viene eseguita al posto dell'analisi della stringa di eccezione che impatterebbe negativamente sulle prestazioni e sulla manutenzione.

    Si rivela ad esempio utile definire un'eccezione FileNotFoundException, poiché lo sviluppatore potrebbe decidere di creare il file mancante. L'elemento FileIOException, tuttavia, non viene in genere gestito in modo specifico nel codice.

  • Non derivare tutte le nuove eccezioni direttamente dalla classe base SystemException. Ereditare da SystemException solo in caso di creazione di nuove eccezioni negli spazi dei nomi System. Ereditare da ApplicationException in caso di creazione di nuove eccezioni in altri spazi dei nomi.

  • Raggruppare le nuove eccezioni derivate da SystemException o ApplicationException in base allo spazio dei nomi. Tutte le eccezioni di System.IO, ad esempio, vengono raggruppate in IOException (derivata da SystemException) e tutte le eccezioni di Microsoft.Media potrebbero essere raggruppate in MediaException (derivata da ApplicationException).

  • Utilizzare una stringa descrittiva localizzata in ciascuna eccezione. Il messaggio di errore visualizzato per l'utente verrà derivato dalla stringa descrittiva dell'eccezione che era stata generata e, in nessun caso, dalla classe di eccezione.

  • Creare messaggi di errore con punteggiatura corretti dal punto di vista grammaticale. Ciascuna frase nella stringa descrittiva di un'eccezione deve terminare con un punto. Il codice che visualizza genericamente un messaggio di eccezione all'utente non deve gestire l'uso di maiuscole e minuscole quando uno sviluppatore ha dimenticato di inserire il punto finale.

  • Fornire le proprietà delle eccezioni per l'accesso a livello di codice. Includere informazioni aggiuntive, diverse dalla stringa descrittiva, in un'eccezione solo se è previsto uno scenario a livello di codice in cui tali informazioni sono utili. È raro che sia necessario includere informazioni aggiuntive in un'eccezione.

  • Non esporre informazioni protette nei messaggi di eccezione. Informazioni come i percorsi nel file system locale vengono considerate informazioni privilegiate, che potrebbero essere utilizzate per raccogliere informazioni riservate sull'utente dal computer mediante codice dannoso.

  • Non utilizzare le eccezioni per gli errori normali o previsti o per il normale flusso di controllo.

  • Restituire null per i casi di errori molto comuni. Un comando File.Open, ad esempio, restituisce un riferimento null se il file non viene individuato, ma genera un'eccezione se il file è bloccato.

  • Progettare le classi in modo che durante il normale corso d'uso non venga mai generata un'eccezione. Nell'esempio di codice seguente una classe FileStream espone un'altra modalità per determinare se è stata raggiunta la fine del file al fine di evitare che venga generata un'eccezione se lo sviluppatore legge oltre la fine del file.

    Class FileRead 
       Sub Open() 
          Dim stream As FileStream = File.Open("myfile.txt", FileMode.Open)
          Dim b As Byte
    
          ' ReadByte returns -1 at end of file.
          While b = stream.ReadByte() <> true
             ' Do something.
          End While
       End Sub
    End Class
    [C#]
    class FileRead 
    {
       void Open() 
       {
          FileStream stream = File.Open("myfile.txt", FileMode.Open);
          byte b;
    
          // ReadByte returns -1 at end of file.
          while ((b = stream.ReadByte()) != true) 
          {
             // Do something.
          }
       }
    }
    
  • Generare l'eccezione InvalidOperationException se una chiamata a un metodo o una funzione di accesso set della proprietà non è appropriata allo stato corrente dell'oggetto.

  • Generare un'eccezione ArgumentException o creare un'eccezione derivata da questa classe se vengono passati o rilevati parametri non validi.

  • Si noti che l'analisi dello stack inizia nel punto in cui viene generata un'eccezione, non nel punto in cui essa viene creata con l'operatore new. Tenere in considerazione questo aspetto quando si decide il punto in cui generare un'eccezione.

  • Utilizzare metodi per la creazione di eccezioni. Una classe genera spesso la stessa eccezione da punti diversi dell'implementazione. Per evitare codice ripetitivo, utilizzare metodi di supporto che consentono di creare la stringa di eccezione utilizzando l'operatore new e restituirla. Nell'esempio di codice seguente viene illustrata la modalità di implementazione di un metodo di supporto.

    class File 
    {
       string fileName;  
       public byte[] Read(int bytes) 
       {
          if (!ReadFile(handle, bytes))
                throw NewFileIOException();
       }
    
       FileException NewFileIOException() 
       {
          string description = 
             // Build localized string, include fileName.
          return new FileException(description);
       }
    }
    
  • Generare eccezioni anziché restituire un codice di errore o HRESULT.

  • Generare l'eccezione più specifica possibile.

  • Creare un testo del messaggio significativo per le eccezioni, destinato allo sviluppatore.

  • Impostare tutti i campi sull'eccezione utilizzata.

  • Utilizzare le eccezioni interne (eccezioni concatenate). Intercettare o generare nuovamente le eccezioni solo se si desidera aggiungere ulteriori informazioni o modificare il tipo dell'eccezione.

  • Non creare metodi che generano un'eccezione NullReferenceException o IndexOutOfRangeException.

  • Eseguire il controllo degli argomenti sui membri protected (Family) e internal (Assembly). Specificare chiaramente nella documentazione se il metodo protected non esegue il controllo degli argomenti. Salvo indicazioni diverse, presumere che il controllo degli argomenti sia stato eseguito. È tuttavia possibile che le prestazioni siano migliori se non si esegue il controllo degli argomenti.

  • Pulire eventuali effetti secondari quando si genera un'eccezione. È necessario che i chiamanti siano in grado di presumere che non esistano effetti secondari quando viene generata un'eccezione da una funzione. Se un metodo Hashtable.Insert, ad esempio, genera un'eccezione, il chiamante può presumere che la voce specificata non sia stata aggiunta a Hashtable.

Tipi di eccezione standard

Nella tabella riportata di seguito vengono elencate le eccezioni standard fornite dall'ambiente di esecuzione e le condizioni per le quali è necessario creare una classe derivata.

Tipo di eccezione Tipo di base Descrizione Esempio
Exception Object Classe base per tutte le eccezioni. Nessuno. Utilizzare una classe derivata di questa eccezione.
SystemException Exception Classe base per tutti gli errori generati nel runtime. Nessuno. Utilizzare una classe derivata di questa eccezione.
IndexOutOfRangeException SystemException Generata nel runtime solo in caso di indicizzazione errata di una matrice. Indicizzazione di una matrice non compresa nell'intervallo valido:

arr[arr.Length+1]

NullReferenceException SystemException Generata nel runtime solo in caso di riferimento a un oggetto null. object o = null;

o.ToString();

InvalidOperationException SystemException Generata da metodi in caso di stato non valido. Chiamata a Enumerator.GetNext() dopo la rimozione di un Item dall'insieme sottostante.
ArgumentException SystemException Classe base per tutte le eccezioni di argomento. Nessuno. Utilizzare una classe derivata di questa eccezione.
ArgumentNullException ArgumentException Generata da metodi che non consentono la presenza di argomenti null. String s = null;

"Calculate".IndexOf (s);

ArgumentOutOfRangeException ArgumentException Generata da metodi che verificano che gli argomenti siano compresi in un determinato intervallo. String s = "string";

s.Chars[9];

ExternalException SystemException Classe base per le eccezioni che si verificano o sono specifiche per ambienti esterni all'ambiente di esecuzione. Nessuno. Utilizzare una classe derivata di questa eccezione.
COMException ExternalException Eccezione che incapsula le informazioni COM Hresult. Utilizzata nell'interoperabilità COM.
SEHException ExternalException Eccezione che incapsula le informazioni per la gestione delle eccezioni strutturate Win32. Utilizzata nel codice Interop non gestito.

Gestione delle eccezioni

Gli errori che si verificano nello stesso livello di un componente devono generare un'eccezione che è significativa per gli utenti di destinazione. Nell'esempio di codice riportato di seguito, il messaggio di errore è specifico per gli utenti della classe TextReader che tentano di eseguire la lettura da un flusso.

Public Class TextReader   
   Public Function ReadLine() As String
      Try
         ' Read a line from the stream.
      Catch e As Exception
         Throw New IOException("Could not read from stream", e)
      End Try
   End Function 
End Class 
[C#]
public class TextReader
{
   public string ReadLine()
   {
      try
      {
         // Read a line from the stream.
      } 
      catch (Exception e)
      {
         throw new IOException ("Could not read from stream", e);
      }
   }
}

Vedere anche

Istruzioni di progettazione per gli sviluppatori di librerie di classi | Gestione e generazione di eccezioni