Condividi tramite


Gestione delle eccezioni (Guida per programmatori C#)

I programmatori C# usano un blocco try per creare partizioni di codice in cui potrebbe essere rilevata un'eccezione. I blocchi catch associati vengono usati per gestire tutte le eccezioni risultanti. Un blocco finally contiene il codice eseguito, indipendentemente dal fatto che venga generata o meno un'eccezione nel blocco try, ad esempio il codice relativo al rilascio delle risorse allocate nel blocco try. Un blocco try richiede uno o più blocchi catch associati, un blocco finally o entrambi.

Gli esempi seguenti illustrano un'istruzione try-catch, un'istruzione try-finally e un'istruzione try-catch-finally.

try
{
    // Code to try goes here.
}
catch (SomeSpecificException ex)
{
    // Code to handle the exception goes here.
    // Only catch exceptions that you know how to handle.
    // Never catch base class System.Exception without
    // rethrowing it at the end of the catch block.
}
try
{
    // Code to try goes here.
}
finally
{
    // Code to execute after the try block goes here.
}
try
{
    // Code to try goes here.
}
catch (SomeSpecificException ex)
{
    // Code to handle the exception goes here.
}
finally
{
    // Code to execute after the try (and possibly catch) blocks
    // goes here.
}

Un blocco try senza un blocco catch o finally genera un errore del compilatore.

Blocchi catch

Un blocco catch consente di specificare il tipo di eccezione da intercettare. La specifica del tipo è chiamata filtro eccezioni. Il tipo di eccezione deve essere derivato da Exception. In genere, non specificare Exception come filtro eccezioni, a meno che non si sappia come gestire tutte le eccezioni che potrebbero essere generate nel blocco try o se si è inclusa un'throwistruzione alla fine del blocco catch.

È possibile concatenare più blocchi catch con classi di eccezioni diversi. I blocchi catch vengono valutati dall'alto verso il basso nel codice, ma viene eseguito un solo blocco catch per ogni eccezione generata, in particolare il primo blocco catch che specifica il tipo esatto o una classe di base dell'eccezione generata. Se nessun blocco catch specifica una ckasse di eccezioni corrispondente, viene selezionato un blocco catch che non dispone di alcun tipo, se presente nell'istruzione. È importante posizionare per primi i blocchi catch con classi di eccezione più specifiche (ossia le più derivate).

Intercettare le eccezioni quando le condizioni seguenti sono vere:

  • Si è compreso il motivo per cui è stata generata l'eccezione ed è possibile implementare un recupero specifico, ad esempio chiedendo all'utente di immettere un nuovo nome di file quando si intercetta un oggetto FileNotFoundException.
  • È possibile creare e generare una nuova eccezione più specifica.
    int GetInt(int[] array, int index)
    {
        try
        {
            return array[index];
        }
        catch (IndexOutOfRangeException e)
        {
            throw new ArgumentOutOfRangeException(
                "Parameter index is out of range.", e);
        }
    }
    
  • Si vuole gestire parzialmente un'eccezione prima di passarla a una maggiore gestione. Nell'esempio seguente un blocco catch viene usato per aggiungere una voce al log degli errori prima di generare di nuovo l'eccezione.
    try
    {
        // Try to access a resource.
    }
    catch (UnauthorizedAccessException e)
    {
        // Call a custom error logging procedure.
        LogError(e);
        // Re-throw the error.
        throw;
    }
    

È anche possibile specificare filtri eccezioni per aggiungere un'espressione booleana a una clausola catch. I filtri eccezioni indicano che una clausola catch specifica corrisponde solo quando tale condizione è vera. Nell'esempio seguente entrambe le clausole catch usano la stessa classe di eccezione, ma viene verificata una condizione aggiuntiva per creare un messaggio di errore diverso:

int GetInt(int[] array, int index)
{
    try
    {
        return array[index];
    }
    catch (IndexOutOfRangeException e) when (index < 0) 
    {
        throw new ArgumentOutOfRangeException(
            "Parameter index cannot be negative.", e);
    }
    catch (IndexOutOfRangeException e)
    {
        throw new ArgumentOutOfRangeException(
            "Parameter index cannot be greater than the array size.", e);
    }
}

Un filtro eccezioni che restituisce sempre false può essere usato per esaminare tutte le eccezioni, ma non elaborarle. Un uso tipico consiste nel registrare le eccezioni:

public class ExceptionFilter
{
    public static void Main()
    {
        try
        {
            string? s = null;
            Console.WriteLine(s.Length);
        }
        catch (Exception e) when (LogException(e))
        {
        }
        Console.WriteLine("Exception must have been handled");
    }

    private static bool LogException(Exception e)
    {
        Console.WriteLine($"\tIn the log routine. Caught {e.GetType()}");
        Console.WriteLine($"\tMessage: {e.Message}");
        return false;
    }
}

Il metodo LogException restituisce sempre false, nessuna clausola catch che usa questo filtro eccezioni corrisponde. La clausola catch può essere generica, usando System.Exception, e le clausole successive possono elaborare classi di eccezioni più specifiche.

Blocchi finally

Un blocco finally consente la pulizia delle azioni eseguite in un blocco try. Se presente, il blocco finally viene eseguito per ultimo, dopo il blocco try e i blocchi catch corrispondenti. Un blocco finally viene sempre eseguito, sia che venga o meno generata un'eccezione o che venga o meno individuato un blocco catch corrispondente al tipo di eccezione.

Il blocco finally può essere usato per rilasciare risorse quali flussi di file, connessioni di database e handle di elementi grafici, senza attendere che il Garbage Collector del runtime finalizzi gli oggetti.

Nell'esempio riportato di seguito viene usato il blocco finally per chiudere un file aperto nel blocco try. Si noti che prima della chiusura viene controllato lo stato dell'handle del file. Se il blocco try non riesce ad aprire il file, l'handle del file ha ancora valore null e il blocco finally non tenta di chiuderlo. In alternativa, se il file è aperto correttamente nel blocco try, il blocco finally chiude il file aperto.

FileStream? file = null;
FileInfo fileinfo = new System.IO.FileInfo("./file.txt");
try
{
    file = fileinfo.OpenWrite();
    file.WriteByte(0xF);
}
finally
{
    // Check for null because OpenWrite might have failed.
    file?.Close();
}

Specifiche del linguaggio C#

Per altre informazioni, vedere Eccezioni e Istruzione throw in Specifica del linguaggio C#. La specifica del linguaggio costituisce il riferimento ufficiale principale per la sintassi e l'uso di C#.

Vedi anche