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'throw
istruzione 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#.