Esercizio - Intercettare tipi di eccezione specifici
In precedenza in questo modulo si è appreso che gli oggetti eccezione rilevati dall'applicazione C# sono istanze di una classe di eccezione. In generale, il codice sarà catch uno dei seguenti:
- Oggetto eccezione che rappresenta un'istanza della
System.Exceptionclasse di base. - Oggetto eccezione che è un'istanza di un tipo di eccezione che eredita dalla classe di base. Ad esempio, un'istanza della
InvalidCastExceptionclasse .
Esaminare le proprietà delle eccezioni
System.Exception è la classe base da cui ereditano tutti i tipi di eccezione derivati. Ogni tipo di eccezione eredita dalla classe base tramite una gerarchia di classi specifica. Ad esempio, la gerarchia di classi per è InvalidCastException la seguente:
Object
Exception
SystemException
InvalidCastException
La maggior parte delle classi di eccezioni che ereditano da Exception non aggiungono funzionalità aggiuntive, ma ereditano semplicemente da Exception. Pertanto, esaminando le proprietà della Exception classe è possibile comprendere la maggior parte delle eccezioni e come usare un'eccezione nel codice.
Ecco le proprietà della Exception classe :
-
Dati: la
Dataproprietà contiene dati arbitrari nelle coppie chiave-valore. -
HelpLink: la
HelpLinkproprietà può essere usata per contenere un URL (o URN) in un file della Guida che fornisce informazioni complete sulla causa di un'eccezione. -
HResult: la
HResultproprietà può essere usata per accedere a un valore numerico codificato assegnato a un'eccezione specifica. -
InnerException: la
InnerExceptionproprietà può essere usata per creare e mantenere una serie di eccezioni durante la gestione delle eccezioni. -
Messaggio: la
Messageproprietà fornisce informazioni dettagliate sulla causa di un'eccezione. -
Origine: la
Sourceproprietà può essere utilizzata per accedere al nome dell'applicazione o all'oggetto che causa l'errore. -
StackTrace: la
StackTraceproprietà contiene un'analisi dello stack che può essere usata per determinare dove si è verificato un errore. -
TargetSite: la
TargetSiteproprietà può essere usata per ottenere il metodo che genera l'eccezione corrente.
Va bene se ci si sente un po' sopraffatti da questo esame delle proprietà delle eccezioni, delle classi di base e dell'ereditarietà. Non preoccuparti, intercettare le eccezioni nel codice e accedere alle proprietà di un'eccezione sono più semplici rispetto alla spiegazione del funzionamento delle eccezioni e delle proprietà delle eccezioni.
Annotazioni
In questo modulo verrà illustrato l'uso della proprietà del messaggio di un'eccezione per segnalare l'eccezione nell'interfaccia utente dell'applicazione.
Accedere alle proprietà di un oggetto eccezione
Ora che si conoscono gli oggetti eccezione e le relative proprietà, è possibile iniziare a scrivere codice.
Aggiornare il file Program.cs come segue:
try { Process1(); } catch { Console.WriteLine("An exception has occurred"); } Console.WriteLine("Exit program"); static void Process1() { try { WriteMessage(); } catch { Console.WriteLine("Exception caught in Process1"); } } static void WriteMessage() { double float1 = 3000.0; double float2 = 0.0; int number1 = 3000; int number2 = 0; Console.WriteLine(float1 / float2); Console.WriteLine(number1 / number2); }Prenditi un minuto per esaminare il codice.
Si tratta dello stesso codice visualizzato nell'unità precedente (il codice della soluzione per l'attività di verifica). Si sa che viene generata un'eccezione durante il
WriteMessagemetodo . Si sa anche che l'eccezione viene intercettata nelProcess1metodo . Questo codice verrà usato per esaminare gli oggetti eccezione e i tipi di eccezione specifici.Aggiornare il metodo
Process1come segue:static void Process1() { try { WriteMessage(); } catch (Exception ex) { Console.WriteLine($"Exception caught in Process1: {ex.Message}"); } }Esaminare gli aggiornamenti.
Si noti che la clausola aggiornata
catchrileva un'istanza dellaExceptionclasse in un oggetto denominatoex. Si noti anche che ilConsole.WriteLine()metodo usaexper accedere alla proprietà dell'oggettoMessagee visualizzare il messaggio di errore nella console.Anche se la
catchclausola può essere usata senza argomenti, tale approccio non è consigliato. Se non si specifica un argomento, tutti i tipi di eccezione vengono intercettati e non è possibile decernare tra di essi.In generale, è consigliabile rilevare solo le eccezioni da cui il codice sa come eseguire il ripristino. Pertanto, la
catchclausola deve specificare un argomento dell'oggetto derivato daSystem.Exception. Il tipo di eccezione deve essere il più specifico possibile. Ciò consente di evitare di intercettare le eccezioni che il gestore eccezioni non è in grado di risolvere. Il codice verrà aggiornato per intercettare un tipo di eccezione specifico più avanti in questo esercizio.Nel menu File, selezionare Salva.
Impostare il punto di interruzione nella riga di codice seguente:
Console.WriteLine($"Exception caught in Process1: {ex.Message}");Nel menu Esegui selezionare Avvia debug
L'esecuzione del codice deve essere sospesa in corrispondenza del punto di interruzione.
Passare il cursore del mouse su
ex.Si noti che IntelliSense visualizza le stesse proprietà di eccezione esaminate in precedenza.
Esaminare le informazioni che descrivono l'oggetto
execcezione .Si noti che l'eccezione è un
System.DivideByZeroExceptiontipo di eccezione e che laMessageproprietà è impostata suAttempted to divide by zero..Si noti che la
StackTraceproprietà segnala il metodo e il numero di riga in cui si è verificato l'errore, insieme alla sequenza di chiamate al metodo (e ai numeri di riga) che hanno causato l'errore.Sulla barra degli strumenti Debug selezionare Continua.
Esaminare l'output della console.
Si noti che la proprietà dell'eccezione
Messageè inclusa nell'output generato dall'applicazione:∞ Exception caught in Process1: Attempted to divide by zero. Exit program
Intercettare un tipo di eccezione specifico
Ora che si conosce il tipo di eccezione da intercettare, è possibile aggiornare la catch clausola per gestire tale tipo di eccezione specifico.
Aggiornare il metodo
Process1come segue:static void Process1() { try { WriteMessage(); } catch (DivideByZeroException ex) { Console.WriteLine($"Exception caught in Process1: {ex.Message}"); } }Salvare il codice e quindi avviare una sessione di debug.
Si noti che l'applicazione aggiornata segnala gli stessi messaggi alla console.
Anche se i messaggi segnalati sono gli stessi, esiste una differenza importante. Il
Process1metodo intercetta solo le eccezioni del tipo specifico che è pronto per gestire.Per generare un tipo di eccezione diverso, aggiornare il
WriteMessagemetodo nel modo seguente:static void WriteMessage() { double float1 = 3000.0; double float2 = 0.0; int number1 = 3000; int number2 = 0; byte smallNumber; Console.WriteLine(float1 / float2); // Console.WriteLine(number1 / number2); checked { smallNumber = (byte)number1; } }Si noti l'uso dell'istruzione
checked.Quando si eseguono calcoli di tipo integrale che assegnano il valore di un tipo integrale a un altro tipo integrale, il risultato dipende dal contesto di controllo dell'overflow. In un
checkedcontesto la conversione ha esito positivo se il valore di origine è compreso nell'intervallo del tipo di destinazione. In caso contrario, verrà generata un'eccezioneOverflowException. In un contesto deselezionato, la conversione ha sempre esito positivo e procede come segue:Se il tipo di origine è maggiore del tipo di destinazione, il valore di origine viene troncato rimuovendo i bit più significativi "extra". Il risultato viene quindi trattato come un valore del tipo di destinazione.
Se il tipo di origine è minore del tipo di destinazione, il valore di origine è esteso o esteso a zero in modo che corrisponda alla stessa dimensione del tipo di destinazione. L'estensione del segno viene usata se il tipo di origine è firmato; zero-extension viene usato se il tipo di origine non è firmato. Il risultato viene quindi trattato come un valore del tipo di destinazione.
Se il tipo di origine ha le stesse dimensioni del tipo di destinazione, il valore di origine viene considerato come valore del tipo di destinazione.
Annotazioni
I calcoli di tipo integrale che non si trovano all'interno di un
checkedblocco di codice vengono considerati come se fossero all'interno di ununcheckedblocco di codice.Salvare il codice e quindi avviare una sessione di debug.
Si noti che un nuovo tipo di eccezione viene intercettato dalla
catchclausola nelle istruzioni di primo livello anziché all'interno delProcess1metodo .L'applicazione stampa i messaggi seguenti nella console:
∞ An exception has occurred Exit programAnnotazioni
Il
catchblocco inProcess1non viene eseguito. Questo è il comportamento desiderato. Rilevare solo le eccezioni che il codice è pronto per gestire.
Intercettare più eccezioni in un blocco di codice
A questo punto, è possibile chiedersi cosa accade quando si verificano più eccezioni in un singolo blocco di codice. Ogni eccezione verrà codificata catch man mano che si verificano?
Aggiornare il metodo
WriteMessagecome segue:static void WriteMessage() { double float1 = 3000.0; double float2 = 0.0; int number1 = 3000; int number2 = 0; byte smallNumber; Console.WriteLine(float1 / float2); Console.WriteLine(number1 / number2); checked { smallNumber = (byte)number1; } }Impostare il punto di interruzione all'interno del
WriteMessage()metodo nella riga di codice seguente:Console.WriteLine(float1 / float2);Salvare il codice e quindi avviare una sessione di debug.
Eseguire il codice una riga alla volta e osservare cosa accade dopo che il codice gestisce la prima eccezione.
Quando si verifica la prima eccezione, il controllo viene passato alla prima
catchclausola che può gestire l'eccezione. Il codice che genera la seconda eccezione non viene mai raggiunto. Ciò significa che alcuni del codice non vengono mai eseguiti. Questo potrebbe causare gravi problemi.Prendere in considerazione il modo in cui è possibile gestire più eccezioni e quando/perché il codice potrebbe non gestire più eccezioni.
Si è appreso in precedenza in questo modulo che le eccezioni devono essere rilevate il più vicino possibile alla posizione in cui si verificano. Tenendo presente questo aspetto, è possibile scegliere di aggiornare il
WriteMessagemetodo per intercettare le eccezioni usando il propriotry-catch. Per esempio:static void WriteMessage() { double float1 = 3000.0; double float2 = 0.0; int number1 = 3000; int number2 = 0; byte smallNumber; try { Console.WriteLine(float1 / float2); Console.WriteLine(number1 / number2); } catch (DivideByZeroException ex) { Console.WriteLine($"Exception caught in WriteMessage: {ex.Message}"); } checked { smallNumber = (byte)number1; } }È anche possibile eseguire il wrapping del codice che causa l'oggetto
OverflowExceptionin un oggetto separatotry-catchall'interno delWriteMessage()metodo .checked { try { smallNumber = (byte)number1; } catch (OverflowException ex) { Console.WriteLine($"Exception caught in WriteMessage: {ex.Message}"); } }In quali condizioni sarebbe indesiderabile rilevare le eccezioni successive?
Si consideri il caso in cui il metodo (o il blocco di codice) stia completando un processo in due parti. Si supponga che la seconda parte del processo sia dipendente dal completamento della prima parte. Se la prima parte del processo non è in grado di completare correttamente, non è necessario continuare con la seconda parte del processo. In questo caso, è spesso preferibile presentare all'utente un messaggio che spiega la condizione di errore senza tentare la parte o le parti rimanenti del processo più grande.
Intercettare tipi di eccezione separati in un blocco di codice
In alcuni casi le variazioni nei dati possono causare diversi tipi di eccezioni.
Cancellare i punti di interruzione e quindi sostituire il contenuto del file Program.cs con il codice seguente:
// inputValues is used to store numeric values entered by a user string[] inputValues = new string[]{"three", "9999999999", "0", "2" }; foreach (string inputValue in inputValues) { int numValue = 0; try { numValue = int.Parse(inputValue); } catch (FormatException) { Console.WriteLine("Invalid readResult. Please enter a valid number."); } catch (OverflowException) { Console.WriteLine("The number you entered is too large or too small."); } catch(Exception ex) { Console.WriteLine(ex.Message); } }Esaminare questo codice.
Prima di tutto, il codice crea una matrice di stringhe denominata
inputValues. I dati nella matrice devono rappresentare i valori di input immessi da un utente che ha richiesto di immettere valori numerici. A seconda del valore immesso, possono verificarsi tipi di eccezione diversi.Si noti che il codice usa il
int.Parsemetodo per convertire i valori stringa "input" in numeri interi. Ilint.Parsecodice viene inserito all'interno di un blocco ditrycodice.Impostare un punto di interruzione nella riga di codice seguente:
int numValue = 0;Salvare il codice e quindi avviare una sessione di debug.
Esaminare il codice una riga alla volta e notare che vengono rilevati diversi tipi di eccezione.
Riepilogo
Ecco i concetti più importanti di questa unità da ricordare:
- La
catchclausola deve essere configurata per intercettare un tipo di eccezione specifico. Ad esempio, ilDivideByZeroExceptiontipo di eccezione. - È possibile accedere alle proprietà di un oggetto eccezione all'interno del
catchblocco. Ad esempio, è possibile usare laMessageproprietà per informare l'utente dell'applicazione di un problema. - È possibile specificare due o più
catchclausole quando è necessario intercettare più tipi di eccezione.