Esaminare come creare e generare eccezioni in C#
- 16 minuti
.NET fornisce una gerarchia di classi di eccezioni che derivano dalla System.Exception classe di base. Le applicazioni C# possono creare e generare eccezioni di qualsiasi tipo di eccezione. Gli sviluppatori possono anche personalizzare gli oggetti eccezione con informazioni specifiche dell'applicazione assegnando valori di proprietà.
Annotazioni
Questo modulo è incentrato sulla creazione e la generazione di eccezioni e sulla personalizzazione degli oggetti eccezione. La creazione di classi di eccezioni personalizzate non rientra nell'ambito di questo modulo.
Creare un oggetto eccezione
La creazione e la generazione di eccezioni dall'interno del codice sono un aspetto importante della programmazione C#. La possibilità di generare un'eccezione in risposta a una condizione, a un problema o a un errore specifico consente di garantire la stabilità dell'applicazione.
Il tipo di eccezione creato dipende dal problema di codifica e deve corrispondere allo scopo previsto dell'eccezione il più possibile.
Si supponga, ad esempio, di creare un metodo denominato GraphData che esegue l'analisi dei dati. Il metodo riceve una matrice di dati come parametro di input. Il metodo prevede che i dati di input siano inclusi in un intervallo specifico. Se il metodo riceve dati esterni all'intervallo previsto, crea e genera un'eccezione di tipo ArgumentException. L'eccezione verrà gestita in un punto inferiore dello stack di chiamate dal codice responsabile della fornitura dei dati.
Ecco alcuni tipi di eccezione comuni che è possibile usare durante la creazione di un'eccezione:
ArgumentExceptionoArgumentNullException: usare questi tipi di eccezione quando viene chiamato un metodo o un costruttore con un valore di argomento non valido o un riferimento Null.InvalidOperationException: usare questo tipo di eccezione quando le condizioni operative di un metodo non supportano il completamento corretto di una determinata chiamata al metodo.NotSupportedException: usare questo tipo di eccezione quando un'operazione o una funzionalità non è supportata.IOException: usare questo tipo di eccezione quando un'operazione di input/output ha esito negativo.FormatException: usare questo tipo di eccezione quando il formato di una stringa o di dati non è corretto.
La new parola chiave viene usata per creare un'istanza di un'eccezione. Ad esempio, è possibile creare un'istanza del ArgumentException tipo di eccezione come indicato di seguito:
ArgumentException invalidArgumentException = new ArgumentException();
Configurare e lanciare eccezioni personalizzate
Il processo di generazione di un oggetto eccezione comporta la creazione di un'istanza di una classe derivata da eccezioni, la configurazione facoltativa delle proprietà dell'eccezione e la generazione dell'oggetto tramite la throw parola chiave .
Spesso è utile personalizzare un'eccezione con informazioni contestuali prima che venga generata. È possibile fornire informazioni specifiche dell'applicazione all'interno di un oggetto eccezione configurandone le proprietà. Ad esempio, il codice seguente crea un oggetto eccezione denominato invalidArgumentException con una proprietà personalizzata Message e quindi genera l'eccezione:
ArgumentException invalidArgumentException = new ArgumentException("ArgumentException: The 'GraphData' method received data outside the expected range.");
throw invalidArgumentException;
Annotazioni
La Message proprietà di un'eccezione è di sola lettura. Pertanto, è necessario impostare una proprietà personalizzata Message quando si crea un'istanza dell'oggetto.
Quando si personalizza un oggetto eccezione, è importante fornire messaggi di errore chiari che descrivono il problema e come risolverlo. È anche possibile includere informazioni aggiuntive, ad esempio analisi dello stack e codici di errore, per aiutare gli utenti a risolvere il problema.
È anche possibile creare un oggetto eccezione direttamente all'interno di un'istruzione throw . Per esempio:
throw new FormatException("FormatException: Calculations in process XYZ have been cancelled due to invalid data format.");
Alcune considerazioni da tenere presenti quando si genera un'eccezione includono:
- La
Messageproprietà deve spiegare il motivo dell'eccezione. Tuttavia, le informazioni sensibili o che rappresentano un problema di sicurezza non devono essere inserite nel testo del messaggio. - La
StackTraceproprietà viene spesso utilizzata per tenere traccia dell'origine dell'eccezione. Questa proprietà stringa contiene il nome dei metodi nello stack di chiamate corrente, insieme al nome del file e al numero di riga di ogni metodo, ciascuno dei quali è associato all'eccezione. UnStackTraceoggetto viene creato automaticamente da Common Language Runtime (CLR) dal punto dell'istruzionethrow. Le eccezioni devono essere generate dal punto in cui deve iniziare l'analisi dello stack.
Quando generare un'eccezione
I metodi devono generare un'eccezione ogni volta che non possono completare lo scopo previsto. L'eccezione generata deve essere basata sull'eccezione più specifica disponibile che soddisfa le condizioni di errore.
Si consideri uno scenario in cui uno sviluppatore sta lavorando a un'applicazione che implementa un processo aziendale. Il processo aziendale dipende dall'input dell'utente. Se l'input non corrisponde al tipo di dati previsto, il metodo che implementa il processo aziendale crea e genera un'eccezione. L'oggetto eccezione può essere configurato con informazioni specifiche dell'applicazione nei valori delle proprietà. L'esempio di codice seguente illustra lo scenario:
string[][] userEnteredValues = new string[][]
{
new string[] { "1", "two", "3"},
new string[] { "0", "1", "2"}
};
foreach (string[] userEntries in userEnteredValues)
{
try
{
BusinessProcess1(userEntries);
}
catch (Exception ex)
{
if (ex.StackTrace.Contains("BusinessProcess1") && (ex is FormatException))
{
Console.WriteLine(ex.Message);
}
}
}
static void BusinessProcess1(string[] userEntries)
{
int valueEntered;
foreach (string userValue in userEntries)
{
try
{
valueEntered = int.Parse(userValue);
// completes required calculations based on userValue
// ...
}
catch (FormatException)
{
FormatException invalidFormatException = new FormatException("FormatException: User input values in 'BusinessProcess1' must be valid integers");
throw invalidFormatException;
}
}
}
In questo esempio di codice, le istruzioni di primo livello chiamano il BusinessProcess1 metodo , passando una matrice di stringhe contenente i valori immessi dall'utente. Il BusinessProcess1 metodo prevede valori di input utente che possono essere convertiti in un numero intero. Quando il metodo rileva dati con un formato non valido, crea un'istanza del FormatException tipo di eccezione usando una proprietà personalizzata Message . Il metodo genera quindi l'eccezione. L'eccezione viene rilevata nelle istruzioni di primo livello come oggetto denominato ex. Le proprietà dell'oggetto ex vengono esaminate prima di visualizzare il messaggio di eccezione all'utente. Prima di tutto, il codice esamina la StackTrace proprietà per verificare se contiene "BusinessProcess1". In secondo luogo, l'oggetto ex eccezione viene verificato come di tipo FormatException.
Generare nuovamente eccezioni
Oltre a generare una nuova eccezione, throw può essere utilizzato per rilanciare un'eccezione da un blocco di codice catch. In questo caso, throw non accetta un operando di eccezione.
catch (Exception ex)
{
// handle or partially handle the exception
// ...
// re-throw the original exception object for further handling down the call stack
throw;
}
Quando si genera nuovamente un'eccezione, viene usato l'oggetto eccezione originale, quindi non si perdono informazioni sull'eccezione. Se si desidera creare un nuovo oggetto eccezione che esegue il wrapping dell'eccezione originale, è possibile passare l'eccezione originale come argomento al costruttore di un nuovo oggetto eccezione. Per esempio:
catch (Exception ex)
{
// handle or partially handle the exception
// ...
// create a new exception object that wraps the original exception
throw new ApplicationException("An error occurred", ex);
}
Per lo scenario dell'applicazione "BusinessProcess1", prendere in considerazione gli aggiornamenti seguenti:
- Il
BusinessProcess1metodo è stato aggiornato per includere dettagli aggiuntivi.BusinessProcess1ora rileva due problemi e deve generare eccezioni per ogni problema. - Le dichiarazioni di primo livello sono state aggiornate. Le istruzioni di primo livello chiamano ora il
OperatingProcedure1metodo .OperatingProcedure1chiamaBusinessProcess1all'interno di un blocco ditrycodice. - Il
OperatingProcedure1metodo è in grado di gestire uno dei tipi di eccezione e di gestire parzialmente l'altro. Dopo l'elaborazione dell'eccezione parzialmente gestita, è necessario cheOperatingProcedure1generi nuovamente l'eccezione originale.
L'esempio di codice seguente illustra lo scenario aggiornato:
try
{
OperatingProcedure1();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine("Exiting application.");
}
static void OperatingProcedure1()
{
string[][] userEnteredValues = new string[][]
{
new string[] { "1", "two", "3"},
new string[] { "0", "1", "2"}
};
foreach(string[] userEntries in userEnteredValues)
{
try
{
BusinessProcess1(userEntries);
}
catch (Exception ex)
{
if (ex.StackTrace.Contains("BusinessProcess1"))
{
if (ex is FormatException)
{
Console.WriteLine(ex.Message);
Console.WriteLine("Corrective action taken in OperatingProcedure1");
}
else if (ex is DivideByZeroException)
{
Console.WriteLine(ex.Message);
Console.WriteLine("Partial correction in OperatingProcedure1 - further action required");
// re-throw the original exception
throw;
}
else
{
// create a new exception object that wraps the original exception
throw new ApplicationException("An error occurred - ", ex);
}
}
}
}
}
static void BusinessProcess1(string[] userEntries)
{
int valueEntered;
foreach (string userValue in userEntries)
{
try
{
valueEntered = int.Parse(userValue);
checked
{
int calculatedValue = 4 / valueEntered;
}
}
catch (FormatException)
{
FormatException invalidFormatException = new FormatException("FormatException: User input values in 'BusinessProcess1' must be valid integers");
throw invalidFormatException;
}
catch (DivideByZeroException)
{
DivideByZeroException unexpectedDivideByZeroException = new DivideByZeroException("DivideByZeroException: Calculation in 'BusinessProcess1' encountered an unexpected divide by zero");
throw unexpectedDivideByZeroException;
}
}
}
Il codice di esempio aggiornato produce l'output seguente:
FormatException: User input values in 'BusinessProcess1' must be valid integers
Corrective action taken in OperatingProcedure1
DivideByZeroException: Calculation in 'BusinessProcess1' encountered an unexpected divide by zero
Partial correction in OperatingProcedure1 - further action required
DivideByZeroException: Calculation in 'BusinessProcess1' encountered an unexpected divide by zero
Exiting application.
Aspetti da evitare quando si generano eccezioni
L'elenco seguente identifica le procedure da evitare quando si generano eccezioni:
- Non usare le eccezioni per modificare il flusso di un programma come parte dell'esecuzione normale. Usare le eccezioni per segnalare e gestire le condizioni di errore.
- Le eccezioni non devono essere restituite come valore restituito o parametro anziché essere generate.
- Non lanciare
System.Exception,System.SystemException,System.NullReferenceExceptionoSystem.IndexOutOfRangeExceptionintenzionalmente dal tuo codice sorgente. - Non creare eccezioni che possono essere generate in modalità di debug ma non in modalità di rilascio. Per identificare gli errori di runtime durante la fase di sviluppo, usare
Debug.Assertinvece .
Annotazioni
Il Debug.Assert metodo è uno strumento per rilevare gli errori logici durante lo sviluppo. Per impostazione predefinita, il Debug.Assert metodo funziona solo nelle compilazioni di debug. È possibile usare Debug.Assert nelle sessioni di debug per verificare la presenza di una condizione che non deve mai verificarsi. Il metodo accetta due parametri: una condizione booleana da controllare e un messaggio stringa facoltativo da visualizzare se la condizione è false. Debug.Assert non deve essere usato al posto di generare un'eccezione, che serve a gestire situazioni eccezionali durante l'esecuzione normale del tuo codice. È consigliabile usare Debug.Assert per rilevare gli errori che non devono mai verificarsi e usare le eccezioni per gestire gli errori che potrebbero verificarsi durante l'esecuzione normale del programma.
Riepilogo
Ecco i concetti più importanti di questa unità da ricordare:
- Quando si crea e si genera un'eccezione, il tipo di eccezione deve corrispondere allo scopo previsto dell'eccezione il più possibile.
- Per
throwun'eccezione, crei un'istanza di una classe derivata dall'eccezione, configuri le sue proprietà e quindi usi la parola chiavethrow. - Quando si crea un oggetto eccezione, è importante fornire messaggi di errore chiari e informazioni aggiuntive per aiutare gli utenti a risolvere il problema.
Verificare le proprie conoscenze
Commenti e suggerimenti
Questa pagina è stata utile?
No
Serve aiuto con questo argomento?
Provare a usare Ask Learn per chiarire o guidare l'utente in questo argomento?