Cvičení – zachycení konkrétních typů výjimek
Dříve v tomto modulu jste se dozvěděli, že objekty výjimek zachycené vaší aplikací jazyka C# jsou instancemi třídy výjimky. Obecně řečeno, váš kód bude catch mít jednu z následujících možností:
- Objekt výjimky, který je instancí
System.Exceptionzákladní třídy. - Objekt výjimky, který je instancí typu výjimky, který dědí ze základní třídy. Například instance
InvalidCastExceptiontřídy.
Prozkoumání vlastností výjimky
System.Exception je základní třída, ze které dědí všechny odvozené typy výjimek. Každý typ výjimky dědí ze základní třídy prostřednictvím konkrétní hierarchie tříd. Například hierarchie tříd pro tuto InvalidCastException třídu je následující:
Object
Exception
SystemException
InvalidCastException
Většina tříd výjimek, které dědí z Exception , nepřidají žádné další funkce; jednoduše dědí z Exception. Proto zkoumání vlastností Exception třídy umožňuje pochopit většinu výjimek a způsob použití výjimky v kódu.
Tady jsou vlastnosti Exception třídy:
-
Data: Vlastnost
Dataobsahuje libovolná data ve dvojicích klíč-hodnota. -
HelpLink: Vlastnost
HelpLinklze použít k uložení adresy URL (nebo URN) do souboru nápovědy, který poskytuje rozsáhlé informace o příčině výjimky. -
HResult: Vlastnost
HResultlze použít pro přístup k kódované číselné hodnotě, která je přiřazena ke konkrétní výjimce. -
InnerException: Vlastnost
InnerExceptionlze použít k vytvoření a zachování řady výjimek během zpracování výjimek. -
Zpráva: Vlastnost
Messageposkytuje podrobnosti o příčině výjimky. -
Zdroj:
SourceVlastnost lze použít pro přístup k názvu aplikace nebo objektu, který způsobuje chybu. -
StackTrace: Vlastnost
StackTraceobsahuje trasování zásobníku, které lze použít k určení, kde došlo k chybě. -
TargetSite: Vlastnost
TargetSitelze použít k získání metody, která vyvolá aktuální výjimku.
Je v pořádku, jestliže se cítíte trochu zahlceni tímto zkoumáním vlastností výjimek, základních tříd a dědičnosti. Nemějte obavy, zachycení výjimek v kódu a přístup k vlastnostem výjimky je jednodušší než vysvětlit, jak fungují výjimky a vlastnosti výjimky.
Poznámka:
V tomto modulu se zaměříte na použití vlastnosti zprávy výjimky k hlášení výjimky v uživatelském rozhraní vaší aplikace.
Přístup k vlastnostem objektu výjimky
Teď, když rozumíte objektům výjimek a jejich vlastnostem, je čas začít kódovat.
Aktualizujte soubor Program.cs následujícím způsobem:
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); }Projděte si kód za minutu.
Jedná se o stejný kód, který jste viděli v předchozí lekci (kód řešení pro aktivitu výzvy). Víte, že během
WriteMessagemetody je vyvolána výjimka. Také víte, že výjimka je zachycena v metoděProcess1. Tento kód použijete k prozkoumání objektů výjimek a konkrétních typů výjimek.Aktualizujte metodu
Process1následujícím způsobem:static void Process1() { try { WriteMessage(); } catch (Exception ex) { Console.WriteLine($"Exception caught in Process1: {ex.Message}"); } }Prohlédněte si aktualizace za chvilku.
Všimněte si, že aktualizovaná
catchklauzule zachytává instanciExceptiontřídy v objektu s názvemex. Všimněte si také, že vašeConsole.WriteLine()metoda používáexk přístupu k vlastnosti objektuMessagea zobrazuje chybovou zprávu na konzoli.catchI když se klauzule dá použít bez argumentů, tento přístup se nedoporučuje. Pokud argument nezadáte, všechny typy výjimek se zachytí a nelze mezi nimi rozlišovat.Obecně platí, že byste měli zachytit pouze výjimky, ze kterých se váš kód umí zotavit.
catchKlauzule by proto měla určovat argument objektu, který je odvozen zSystem.Exception. Typ výjimky by měl být co nejvíce specifický. To pomáhá vyhnout se zachycení výjimek, které obslužná rutina výjimek nedokáže vyřešit. Kód aktualizujete tak, aby zachytil konkrétní typ výjimky později v tomto cvičení.V nabídce Soubor vyberte Uložit.
Nastavte zarážku na následujícím řádku kódu:
Console.WriteLine($"Exception caught in Process1: {ex.Message}");V nabídce Spustit vyberte Spustit ladění.
Provádění kódu by se mělo pozastavit na bodu přerušení.
Najeďte myší na
ex.Všimněte si, že IntelliSense zobrazuje stejné vlastnosti výjimek, které jste prozkoumali dříve.
Chvíli se podívejte na informace popisující objekt
exvýjimky .Všimněte si, že výjimka je
System.DivideByZeroExceptiontyp výjimky a žeMessagevlastnost je nastavena naAttempted to divide by zero..Všimněte si, že
StackTracevlastnost hlásí metodu a číslo řádku, kde došlo k chybě, spolu s posloupností volání metody (a čísel řádků), která vedla k chybě.Na panelu nástrojů Ladění vyberte Pokračovat.
Prohlédněte si výstup konzoly.
Všimněte si, že vlastnost výjimky
Messageje součástí výstupu vygenerovaného vaší aplikací:∞ Exception caught in Process1: Attempted to divide by zero. Exit program
Zachycení konkrétního typu výjimky
Teď, když znáte typ výjimky, kterou chcete zachytit, můžete aktualizovat catch klauzuli tak, aby zpracovávala tento konkrétní typ výjimky.
Aktualizujte metodu
Process1následujícím způsobem:static void Process1() { try { WriteMessage(); } catch (DivideByZeroException ex) { Console.WriteLine($"Exception caught in Process1: {ex.Message}"); } }Uložte kód a spusťte ladicí relaci.
Všimněte si, že vaše aktualizovaná aplikace hlásí stejné zprávy do konzoly.
I když jsou nahlášené zprávy stejné, existuje důležitý rozdíl. Vaše
Process1metoda zachytí pouze výjimky konkrétního typu, který je připravený zpracovat.Pokud chcete vygenerovat jiný typ výjimky, aktualizujte metodu
WriteMessagenásledujícím způsobem: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; } }Všimněte si použití
checkedpříkazu.Při provádění výpočtů integrálního typu, které přiřazují hodnotu jednoho celočíselného typu jinému celočíselnému typu, závisí výsledek na kontextu kontroly přetečení.
checkedV kontextu bude převod úspěšný, pokud zdrojová hodnota spadá do rozsahu cílového typu. V opačném případě se vyvoláOverflowException. V nezaškrtnutém kontextu bude převod vždy úspěšný a bude pokračovat následujícím způsobem:Pokud je zdrojový typ větší než cílový typ, je zdrojová hodnota zkrácena zrušením jeho "extra" nejvýznamnějších bitů. Výsledek se pak považuje za hodnotu cílového typu.
Pokud je typ zdroje menší než cílový typ, je zdrojová hodnota buď prodloužena, nebo je rozšířena nulou, aby byla stejná jako cílový typ. Rozšíření podpisu se používá, pokud je typ zdroje podepsán; Pokud je typ zdroje nepodepsaný, použije se nulové rozšíření. Výsledek se pak považuje za hodnotu cílového typu.
Pokud je typ zdroje stejná jako cílový typ, je zdrojová hodnota považována za hodnotu cílového typu.
Poznámka:
Výpočty celočíselného
checkedtypu, které nejsou uvnitř bloku kódu, jsou považovány za ty, které jsou uvnitřuncheckedbloku kódu.Uložte váš kód a spusťte ladicí relaci.
Všimněte si, že nový typ výjimky je zachycen
catchklauzulí v příkazech nejvyšší úrovně, nikoli uvnitřProcess1metody.Aplikace vypíše do konzoly následující zprávy:
∞ An exception has occurred Exit programPoznámka:
Blok
catchvProcess1se neprovede. Toto je chování, které jste chtěli. Zachyťte pouze výjimky, které je váš kód připravený zpracovat.
Zachycení více výjimek v bloku kódu
V tuto chvíli vás může zajímat, co se stane, když v jednom bloku kódu dojde k několika výjimkám. Bude váš kód zachytávat každou výjimku catch při jejím výskytu?
Aktualizujte metodu
WriteMessagenásledujícím způsobem: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; } }Nastavte zarážku uvnitř
WriteMessage()metody na následujícím řádku kódu:Console.WriteLine(float1 / float2);Uložte kód a spusťte ladicí relaci.
Procházejte svůj kód jednu řádku po druhé a všimněte si, co se stane poté, co váš kód zpracuje první výjimku.
Když dojde k první výjimce, je ovládací prvek předán první
catchklauzuli, která může zpracovat výjimku. Kód, který by vygeneroval druhou výjimku, není nikdy dosažen. To znamená, že se některý z vašich kódů nikdy nespustí. To by mohlo vést k vážným problémům.Chvíli zvažte, jak můžete spravovat více výjimek a kdy/proč nechcete, aby kód spravil více výjimek.
Dozvěděli jste se dříve v tomto modulu, že výjimky by měly být zachyceny co nejblíže tam, kde se vyskytují. S ohledem na to se můžete rozhodnout aktualizovat metodu
WriteMessage, aby zachytila výjimky pomocí vlastnítry-catch. Například: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; } }Můžete také zabalit kód, který způsobuje
OverflowException, do samostatnéhotry-catchv rámci metodyWriteMessage().checked { try { smallNumber = (byte)number1; } catch (OverflowException ex) { Console.WriteLine($"Exception caught in WriteMessage: {ex.Message}"); } }Za jakých podmínek by bylo nežádoucí zachytit další výjimky?
Vezměte v úvahu případ, kdy vaše metoda (nebo blok kódu) provádí dva části procesu. Předpokládejme, že druhá část procesu závisí na dokončení první části. Pokud se první část procesu nepodaří úspěšně dokončit, není nutné pokračovat na druhou část procesu. V tomto případě je často lepší prezentovat uživatele zprávou s vysvětlením chybového stavu, aniž byste se pokusili o zbývající část nebo části většího procesu.
Zachycení samostatných typů výjimek v bloku kódu
Existují časy, kdy varianty dat můžou způsobit různé typy výjimek.
Vymažte zarážky a nahraďte obsah souboru Program.cs následujícím kódem:
// 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); } }Přečtěte si tento kód za chvilku.
Nejprve kód vytvoří řetězcové pole pojmenované
inputValues. Data v poli představují vstupní hodnoty zadané uživatelem, který byl vyzván k zadání číselných hodnot. V závislosti na zadané hodnotě může dojít k různým typům výjimek.Všimněte si, že kód používá metodu
int.Parsek převodu řetězcových "vstupních" hodnot na celá čísla. Kódint.Parsese umístí dotrybloku kódu.Nastavte zarážku na následujícím řádku kódu:
int numValue = 0;Uložte kód a spusťte ladicí relaci.
Krokujte kódem po jednom řádku a všimněte si, že jsou zachyceny různé typy výjimek.
Rekapitulace
Tady je několik důležitých věcí, které je potřeba si z této lekce zapamatovat:
- Klauzule
catchby měla být nakonfigurovaná tak, aby zachytila určitý typ výjimky. NapříkladDivideByZeroExceptiontyp výjimky. - K vlastnostem objektu výjimky lze přistupovat v rámci
catchbloku. Vlastnost můžete například použítMessagek informování uživatele aplikace o problému. - Pokud potřebujete zachytit více než jeden typ výjimky, můžete zadat dvě nebo více
catchklauzulí.