Gestione delle eccezioni

Le seguenti linee guida contribuiscono a garantire che le eccezioni vengano gestite correttamente dalla libreria che si sta progettando.

Non gestire errori rilevando eccezioni non specifiche, quali System.Exception, System.SystemException e così via, nel codice del framework.

È possibile rilevare eccezioni allo scopo di rigenerarle o trasferirle a un altro thread. Nel codice riportato di seguito viene illustrato un esempio di gestione delle eccezioni non corretta.

Public Class BadExceptionHandlingExample1

    Public Sub DoWork()
        ' Do some work that might throw exceptions.
    End Sub

    Public Sub MethodWithBadHandler()
        Try
            DoWork()
        Catch e As Exception
            ' Handle the exception and 
            ' continue executing.
        End Try
    End Sub
End Class
public class BadExceptionHandlingExample1
{
    public void DoWork()
    {
        // Do some work that might throw exceptions.
    }
    public void MethodWithBadHandler()
    {
        try
        {
            DoWork();
        }
        catch (Exception e)
        {
            // Handle the exception and
            // continue executing.
        }
    }
}
public ref class BadExceptionHandlingExample1
{
public:
    void DoWork()
    {
        // Do some work that might throw exceptions.
    }

    void MethodWithBadHandler()
    {
        try
        {
            DoWork();
        }
        catch (Exception^ e)
        {
            // Handle the exception and
            // continue executing.
        }
    }
};

Evitare di gestire errori rilevando eccezioni non specifiche, quali System.Exception, System.SystemException e così via, nel codice dell'applicazione. La gestione di errori nelle applicazioni è accettabile solo in rari casi.

In un'applicazione non dovrebbero essere gestite eccezioni che possono determinare uno stato imprevisto o vulnerabile. Se non è possibile prevedere tutte le possibili cause di un'eccezione né assicurarsi che lo stato risultante dell'applicazione non possa essere sfruttato da malware, è opportuno consentire che l'applicazione venga terminata anziché gestire l'eccezione.

Non escludere eccezioni speciali quando le eccezioni vengono rilevate a scopo di trasferimento.

Anziché creare elenchi di eccezioni speciali nelle clausole catch, si consiglia di rilevare solo le eccezioni che è possibile gestire in maniera adeguata. Quelle che non possono essere gestite non dovrebbero essere trattate come casi speciali in gestori eccezioni non specifici. Nel codice riportato di seguito viene illustrato un esempio di testing non corretto per eccezioni speciali a scopo di rigenerazione.

Public Class BadExceptionHandlingExample2

    Public Sub DoWork()
        ' Do some work that might throw exceptions.
    End Sub

    Public Sub MethodWithBadHandler()
        Try
            DoWork()
        Catch e As Exception
            If TypeOf e Is StackOverflowException Or _
               TypeOf e Is OutOfMemoryException Then
                Throw
            End If
        End Try
    End Sub
End Class
public class BadExceptionHandlingExample2
{
    public void DoWork()
    {
        // Do some work that might throw exceptions.
    }
    public void MethodWithBadHandler()
    {
        try
        {
            DoWork();
        }
        catch (Exception e)
        {
            if (e is StackOverflowException ||
                e is OutOfMemoryException)
                throw;
            // Handle the exception and
            // continue executing.
        }
    }
}
public ref class BadExceptionHandlingExample2
{
public:
    void DoWork()
    {
        // Do some work that might throw exceptions.
    }

    void MethodWithBadHandler()
    {
        try
        {
            DoWork();
        }
        catch (Exception^ e)
        {
            if (e->GetType() == StackOverflowException::typeid ||
                e->GetType() == OutOfMemoryException::typeid)
                throw;
            // Handle the exception and
            // continue executing.
        }
    }
};

Valutare l'opportunità di rilevare eccezioni specifiche quando si comprende il motivo per cui verranno generate in un determinato contesto.

Si consiglia di rilevare solo le eccezioni che determinano una situazione che è possibile correggere. Ad esempio, un'eccezione FileNotFoundException che risulta da un tentativo di apertura di un file inesistente può essere gestita da un'applicazione perché il problema può essere comunicato all'utente e quest'ultimo può specificare un nome di file differente oppure creare il file da aprire. Una richiesta di apertura di un file che genera un'eccezione ExecutionEngineException non dovrebbe invece essere gestita perché la causa sottostante dell'eccezione non è nota con certezza e l'applicazione non è in grado di garantire che l'esecuzione possa proseguire senza problemi.

Evitare l'utilizzo eccessivo della rilevazione delle eccezioni. È spesso opportuno consentire la propagazione delle eccezioni nei livelli superiori dello stack di chiamate.

Se si rilevano eccezioni che non è possibile gestire in maniera adeguata, verranno nascoste informazioni rilevanti per il debug.

Utilizzare try-finally ed evitare try-catch per il codice di pulizia. Nel codice relativo alle eccezioni scritto correttamente try-finally è molto più comune di try-catch.

Lo scopo di una clausola catch è di consentire la gestione delle eccezioni, ad esempio registrando un errore non irreversibile. Quello di una clausola finally è di consentire l'esecuzione di codice di pulitura indipendentemente dal fatto che sia stata generata un'eccezione. Se si allocano risorse dispendiose o limitate, quali connessioni a database o flussi, si consiglia di inserire il codice per il rilascio di tali risorse all'interno di un blocco finally.

Preferire l'utilizzo di un'istruzione throw vuota quando viene rilevata e rigenerata un'eccezione. Si tratta della tecnica migliore per mantenere lo stack di chiamate dell'eccezione.

Nel codice riportato di seguito viene illustrato un esempio di metodo che può generare un'eccezione. A questo metodo verrà fatto riferimento in esempi successivi.

Public Sub DoWork(ByVal anObject As Object)
    ' Do some work that might throw exceptions.
    If (anObject = Nothing) Then
        Throw New ArgumentNullException("anObject", "Specify a non-null argument.")
    End If
    ' Do work with o.
End Sub
public void DoWork(Object anObject)
{
    // Do some work that might throw exceptions.
    if (anObject == null)
    {
        throw new ArgumentNullException("anObject",
            "Specify a non-null argument.");
    }
    // Do work with o.
}
public:

    void DoWork(Object^ anObject)
    {
        // Do some work that might throw exceptions.
        if (anObject == nullptr)
        {
            throw gcnew ArgumentNullException("anObject",
                "Specify a non-null argument.");
        }
        // Do work with o.
    }

Nel codice riportato di seguito viene illustrato un esempio in cui un'eccezione viene rilevata e specificata in modo non corretto al momento della rigenerazione. In questo modo, la traccia dello stack punterà all'istruzione di rigenerazione come posizione dell'errore anziché puntare al metodo DoWork.

        Public Sub MethodWithBadCatch(ByVal anObject As Object)
            Try
                DoWork(anObject)

            Catch e As ArgumentNullException
                System.Diagnostics.Debug.Write(e.Message)
                ' This is wrong.
                Throw e
                ' Should be this:
                ' throw
            End Try
        End Sub

public void MethodWithBadCatch(Object anObject)
{
    try
    {
        DoWork(anObject);
    }
    catch (ArgumentNullException e)
    {
       System.Diagnostics.Debug.Write(e.Message);
       // This is wrong.
       throw e;
       // Should be this:
       // throw;
    }
}
void MethodWithBadCatch(Object^ anObject)
{
    try
    {
        DoWork(anObject);
    }
    catch (ArgumentNullException^ e)
    {
       System::Diagnostics::Debug::Write(e->Message);
       // This is wrong.
       throw e;
       // Should be this:
       // throw;
    }
}

Non gestire le eccezioni non conformi a CLS, ovvero che non derivano da System.Exception, utilizzando un blocco catch senza parametri. Queste eccezioni possono essere gestite nei linguaggi in cui sono supportate eccezioni non derivate da Exception.

Per le eccezioni non conformi a CLS, in .NET Framework versione 2.0 viene eseguito il wrapping in una classe derivata da Exception.

Portions Copyright 2005 Microsoft Corporation. Tutti i diritti riservati.

Portions Copyright Addison-Wesley Corporation. Tutti i diritti riservati.

Per ulteriori informazioni sulle linee guida di progettazione, vedere “le linee guida di progettazione di Framework: Idiomi convenzioni, e modelli per libro raccolte riutilizzabili .NET„ di Krzysztof Cwalina e brad Abrams, emessi da Addison-Wesley, 2005.

Vedere anche

Altre risorse

Linee guida di progettazione per lo sviluppo di librerie di classi

Linee guida di progettazione delle eccezioni