Suggerimenti per gestire le eccezioni

Aggiornamento: novembre 2007

Un insieme ben progettato di blocchi di codice per la gestione degli errori può rendere un programma più affidabile e meno soggetto ad arresti anomali perché l'applicazione è in grado di gestire tali errori. Di seguito sono indicati alcuni suggerimenti per la gestione delle eccezioni:

  • Sapere quando impostare un blocco try/catch. È ad esempio possibile controllare a livello di codice una condizione che è probabile si verifichi senza ricorrere alla gestione delle eccezioni. In altre situazioni è invece preferibile utilizzare la gestione delle eccezioni per intercettare una condizione di errore.

    Nell'esempio seguente viene utilizzata un'istruzione if per controllare se una connessione è chiusa. È possibile utilizzare questo metodo anziché definire la generazione di un'eccezione in caso di connessione non chiusa.

       If conn.State <> ConnectionState.Closed Then
          conn.Close()
       End If
    
       if(conn.State != ConnectionState.Closed)
          conn.Close();
    

    Nell'esempio che segue viene generata un'eccezione se la connessione non è chiusa.

       Try
          conn.Close()
       Catch ex As InvalidOperationException
          'Do something with the error or ignore it.
       End Try
    
       try {
         conn.Close();
       }
       catch(InvalidOperationException ex) {
         //Do something with the error or ignore it.
       }
    

    Nella scelta del metodo è necessario considerare la frequenza con cui si prevede che l'evento possa verificarsi. Se l'evento è davvero eccezionale e rappresenta un errore, quale la fine imprevista di un file, è preferibile utilizzare la gestione delle eccezioni perché in condizioni normali viene eseguito meno codice. Se l'evento si verifica invece con una certa regolarità, è meglio adottare il metodo a livello di codice per il controllo degli errori. In questo caso, infatti, se si verificasse un'eccezione, la gestione richiederebbe tempi più lunghi.

  • Utilizzare blocchi try/finally attorno al codice che potrebbe, potenzialmente, generare un'eccezione e riunire le istruzioni catch in un'unica posizione centrale. In questo modo l'istruzione try genera l'eccezione, l'istruzione finally chiude o dealloca risorse e l'istruzione catch gestisce l'eccezione da una posizione centrale.

  • Ordinare sempre le eccezioni in blocchi catch dalla più specifica alla meno specifica. In questo modo l'eccezione specifica viene gestita prima di essere passata a un blocco catch più generico.

  • Terminare i nomi delle classi di eccezioni con la parola "Exception". Di seguito è riportato un esempio:

    Public Class EmployeeListNotFoundException
        Inherits Exception
    
    public class MyFileNotFoundException : Exception {
    }
    
  • Quando si creano eccezioni definite dall'utente, è necessario garantire che i metadati relativi alle eccezioni siano disponibili per il codice eseguito in posizione remota, incluso il caso di eccezioni che si verificano a livello di più domini applicazione. Si supponga ad esempio che nel dominio applicazione A venga creato il dominio applicazione B, nel quale viene eseguito codice che genera un'eccezione. Affinché l'eccezione venga intercettata e gestita correttamente nel dominio dell'applicazione A, è necessario che in tale dominio sia possibile trovare l'assembly contenente l'eccezione generata nel dominio dell'applicazione B. Se nel dominio dell'applicazione B viene generata un'eccezione contenuta in un assembly nella base dell'applicazione di tale dominio, ma non nella base dell'applicazione del dominio dell'applicazione A, in quest'ultimo dominio non sarà possibile trovare l'eccezione e in Common Language Runtime verrà generata un'eccezione FileNotFoundException. Per evitare che questo si verifichi è possibile distribuire l'assembly contenente le informazioni sull'eccezione in due modi:

    • Inserendo l'assembly in una base applicativa comune condivisa da entrambi i domini applicazione

      - oppure -

    • Se i domini non condividono alcuna base applicativa comune, firmando l'assembly contenente le informazioni sull'eccezione con un nome sicuro e distribuendo tale assembly nella Global Assembly Cache.

  • In C# e C++ utilizzare almeno i tre costruttori comuni quando si creano classi di eccezione personalizzate. Per un esempio, vedere Procedura: creare eccezioni definite dall'utente.

  • Nella maggior parte dei casi utilizzare i tipi di eccezione predefiniti. Definire nuovi tipi di eccezioni solo per l'utilizzo a livello di codice. Introdurre una nuova classe di eccezione per consentire a un programmatore di eseguire un'operazione differente nel codice in base alla classe di eccezione.

  • Per la maggior parte delle applicazioni, derivare le eccezioni personalizzate dalla classe Exception . L'idea iniziale di far derivare le eccezioni personalizzate dalla classe ApplicationException non ha prodotto alcun valore significativo in termini pratici.

  • Includere in ciascuna eccezione una stringa descrittiva localizzata. Quando viene visualizzato un messaggio di errore per l'utente, tale messaggio verrà tratto dalla stringa descrittiva dell'eccezione generata, anziché dalla classe di eccezione.

  • Utilizzare messaggi di errore corretti dal punto di vista grammaticale, inclusa la punteggiatura finale. È necessario che ogni frase della stringa descrittiva di un'eccezione termini con un punto.

  • Fornire proprietà Exception per l'accesso a livello di codice. Oltre alla stringa descrittiva, includere in un'eccezione informazioni supplementari solo se effettivamente utili nel relativo contesto di codice.

  • Restituire il valore null per i casi di errore estremamente comuni. Open, ad esempio, restituisce null se è impossibile trovare un file, ma genera un'eccezione se il file è bloccato.

  • Progettare le classi in modo che non venga mai generata un'eccezione nell'utilizzo normale. Con la classe FileStream, ad esempio, è possibile determinare in modo diverso se è stata raggiunta la fine del file. Questo consente di evitare l'eccezione che verrebbe generata se si continua la lettura oltre il termine del file. Nell'esempio che segue viene illustrato come terminare la lettura in corrispondenza della fine del file.

    Class FileRead
    
        Public Sub Open(ByVal fileToRead As FileStream)
    
            ' This If statement is optional
            ' as it is very unlikely that
            ' the stream would ever be null
            If IsDBNull(fileToRead) Then
                Throw New System.ArgumentNullException()
            End If
    
            Dim b As Integer
    
            ' Set the stream position to the beginning of the file.
            fileToRead.Seek(0, SeekOrigin.Begin)
    
            ' Read each byte to the end of the file.
            For i As Integer = 0 To fileToRead.Length
                b = fileToRead.ReadByte()
                Console.Write(b.ToString())
                ' Or do something else with the byte.
            Next
        End Sub
    
    End Class
    class FileRead {
        public void Open(FileStream fileToRead) 
        {
    
            // This if statement is optional
            // as it is very unlikely that
            // the stream would ever be null.
            if (fileToRead == null)
            {
                throw new System.ArgumentNullException();
            }
    
            int b;
    
            // Set the stream position to the beginning of the file.
            fileToRead.Seek(0, SeekOrigin.Begin);
    
            // Read each byte to the end of the file.
            for (int i = 0; i < fileToRead.Length; i++)
            {
                b = fileToRead.ReadByte();
                Console.Write(b.ToString());
                // Or do something else with the byte.
            }
        }
    } 
    
  • Generare un'eccezione InvalidOperationException se un insieme di proprietà o una chiamata al metodo non è adatta allo stato corrente dell'oggetto.

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

  • L'analisi dello stack inizia in corrispondenza dell'istruzione in cui l'eccezione viene generata e termina in corrispondenza dell'istruzione catch che intercetta l'eccezione. Tenere presente questo fatto quando si decide in che punto inserire un'istruzione throw.

  • Utilizzare metodi per la creazione di eccezioni. Una classe genera spesso la stessa eccezione da punti diversi dell'implementazione. Per evitare codice di dimensioni eccessive, utilizzare metodi di supporto che creano l'eccezione e la restituiscono. Di seguito è riportato un esempio:

    Class File
       Private fileName As String
    
       Public Function Read(bytes As Integer) As Byte()
          If Not ReadFile(handle, bytes) Then
             Throw NewFileIOException()
          End If
       End Function 'Read
    
       Function NewFileIOException() As FileException
          Dim description As String = __unknown ' Build localized string, including fileName.
          Return New FileException(description) '
       End Function 'NewFileIOException
    End Class 'File
    
    class File {
        string fileName;
        public byte[] Read(int bytes) {
            if (!ReadFile(handle, bytes))
                throw NewFileIOException();
        }
        FileException NewFileIOException() {
            string description = // Build localized string, including fileName.
            return new FileException(description);
         }
    }
    

    In alternativa, utilizzare il costruttore dell'eccezione per creare l'eccezione. Questo è opportuno soprattutto per le classi di eccezione globali, quale ArgumentException.

  • Generare eccezioni anziché restituire un codice di errore o HRESULT.

  • Eliminare i risultati intermedi quando si genera un'eccezione. I chiamanti dovrebbero avere la garanzia che non si verifichino effetti secondari quando un'eccezione viene generata da un metodo.

Vedere anche

Altre risorse

Gestione e generazione di eccezioni