7. Vyvolání výjimky

Někdy se může stát, že výjimku budeme chtít vyvolat sami. Toho docílíme tak, že použijeme následující syntaxi: 

throw new typVýjimky(parametr) 

kde:

  • throw - klíčové slovo příkazu pro vyvolání výjimky
  • new - výjimka je objekt (instance odvozené od třídy), proto klíčové slovo new (představující tzv. konstruktor), které zařídí vznik objektu (mající vlastnosti a metody dané třídy)
  • typVýjimky -  typ výjimky jsme již používali v předchozích případech ošetření výjimek např. typ FormatException, DivideByZeroException apod.
  • parametr - představuje vstup, který může být využit v bloku catch, např. jako parametr můžeme poslat text, popisující příčinu chyby, kterou pak zobrazíme v bloku catch pomocí příkazu ToString pomocí tečkové notace u identifikátoru výjimky

Ilustrativní příklad KalkulackaVyvolaniVyjimky
Program KalkulackaVyvolaniVyjimky

  • Popis: Program pro řešení aritmetických operací u dvou zadaných čísel - vyvolání výjimky
  • Zadání: Zkuste navrhnout hlavní statickou metodu programu KalkulackaVyvolaniVyjimky, která řeší základní aritmetické operace podle zadaného číselného kódu (1 … sčítání, 2 … odečítání, 3 … násobení a 4 … dělení) u dvou uživatelem zadaných reálných čísel a a b. Rozhodování o provedení aritmetické operaci bude provedeno pomocí vícenásobného větvení proměnné kod. Výsledek aritmetické operace vypište v hlavním programu. Vytvořte následující metody:
    • KodOperace – metoda s návratem, která definuje zvolený validní číselný kód operace

    • ValidaceKoduOperace - metoda pro validaci formálního parametru kodOperace. Pokud není kód validní, je vyvolána výjimka typu ArgumentOutOfRangeException

    • Operand – metoda s formálním parametrem text datového typu string a výstupním parametrem cislo, který představuje reálné číslo typu double operandu (čísla) aritmetické operace. Pokud zadané číslo nelze konvertovat pomocí příkazu TryParse bude vyvolána výjimka typu FormatException 

    • AritmetickaOperace – metoda bez návratu, která provede zvolenou příslušnou operaci na základě zvoleného kódu aritmetické operace pomocí vícenásobného větvení a vypíše výsledek aritmetické operace

  • Řešení: KalkulackaVyvolaniVyjimky_Main

Nyní si představíme zdrojový kód statické hlavní metody Main – viz Kód 1. V metodě použijeme příkaz try za účelem možné chyby při načtení špatné hodnoty kódu operace, nebo při konverzi, která může nastat po načtení špatné podoby textového řetězce, který má být konvertován do příslušného reálného čísla. Tento problém může nastat u obou operandů a a b. Tento typ výjimky FormatException jsme pak umístili do příslušného handleru. V metodě jsme využili další typ handleru, tj. ArgumentOutOfRangeException - chyba při zadávání operandů. Třetí typ obecné výjimky bude zachytávat všechny zbylé výjimky, které mohou nastat. Znovu si připomeňme, že musíme být velmi obezřetní při použití tohoto typu výjimky. Poslední příkaz catch bude zachytávat všechny jiné typy výjimek, které nebyly definovány. Čím více tedy budeme definovat konkrétnějších výjimek, tím snáze budeme udržovat kód v pořádku a nacházet přesné příčiny nastalých problémů.

static void Main(string[] args)
{
    //Deklarace lokálních proměnných
    int kod;
    double a, b;
    //Potenciálně nebezpečný kód, který může způsobit výjimku
    try
    {
        //Načtení validního kódu operace
        kod = KodOperace();
        //Načtení validního reálného čísla prvního operandu
        Operand("Zadejte A: ", out a);
        //Načtení validního reálného čísla druhého operandu
        Operand("Zadejte B: ", out b);
        //Volání metody provádějící požadovanou aritmetickou operaci
        AritmetickaOperace(kod, a, b);
        //Čekání na stisk klávesy -> ukončení programu
        Console.ReadKey();
    }
    //Handler chyby při konverzi s parametrem handleru
    catch (FormatException exFormat)
    {
        //Výpis parametru handleru
        Console.WriteLine("Chyba při konverzi: {0}", exFormat.ToString());
    }
    //Handler pro kontrolu rozsahu platných hodnot argumentu s parametrem handleru
    catch (ArgumentOutOfRangeException exArgument)
    {
        //Výpis parametru handleru
        Console.WriteLine("Kód operace: {0}", exArgument.ToString());
    }
    //Obecná výjimka typu Exception (nemusíme specifikovat typ výjimky)
    catch (Exception ex)
    {
        Console.WriteLine("Nastala jiná výjimka: {0}", ex.ToString());
    }
}

Kód 1: Deklarace hlavní statické metoda Main - použití různých typů výjimek

První metoda KodOperace – představuje funkci, tedy metodu s celočíselným návratem, který představuje zvolený validní číselný kód operace – viz Kód 2. V kódu vidíme, že je nejprve uživatel vyzván, aby zadal kód, tedy celé číslo zvolené aritmetické operace. Samotné načtení kódu je provedeno pomocí příkazu ReadLine. Tento příkaz jsme záměrně umístili dovnitř závorky volané metody ValidaceKoduOperace (viz Kód 3), tzn., že tato načtená hodnota představuje skutečný parametr volané metody pro validaci kódu operace. 

//Metoda s návratem načteného validního kódu operace od uživatele
static int KodOperace()
{
    //Výpis výzvy k zadání operace
    Console.Write("Zadejte kód operace (1 ... sčítání, 2 ... odečítání, " +
        "3 ... násobení, 4 ... dělení): ");
    //Načtení kódu operace včetně jeho validace
    return ValidaceKoduOperace(int.Parse(Console.ReadLine()));
}

Kód 2: Deklarace statické metody KodOperace – načtení validního kódu aritmetické operace

Ve zdrojovém kódu volané statické metody s celočíselným návratem ValidaceKoduOperace, vidíme použití podmínky, která testuje, zda je zadán kód operace v mezích intervalu 1 až 4 - viz Kód 3. Pokud je podmínka splněna, vrací metoda načtený kód zpět do volající metody KodOperace. V opačném případě je vyvolána výjimka typu ArgumentOutOfRangeException, tedy že vstupní parametr (argument této metody) je mimo povolený rozsah platných hodnot kódu aritmetické operace. Využili jsme také možnosti přiřazení textu "Neplatný číselný kód operace" do vlastnosti objektu výjimky. Tento text je využit v metodě Main při výpisu příčiny chyby, tj. výpisu vlastnosti objektu výjimky pomocí příkazu ToString (přístup k této vlastnosti je pomocí tečkové notace u daného objektu výjimky typu ArgumentOutOfRangeException v těle příkazu catch).

//Metoda pro validaci načteného kódu operace od uživatele
static int ValidaceKoduOperace(int kodOperace)
{
    //Pokud je kód operace větší nebo rovno 1 a zároveň menší nebo rovno 4
    if ((kodOperace >= 1) && (kodOperace <= 4))
        return kodOperace;
    else
        //Vyvolání výjimky typu argument je mimo rozsah hodnot
        throw new ArgumentOutOfRangeException("Neplaný číselný kód operace");
}

Kód 3: Deklarace statické metody ValidaceKoduOperace – validace načteného kódu aritmetické operace

Protože máme již osvojeny základy práce s parametry u metod, využili jsme v následujícím kódu metody Operand předání vstupního formálního parametru ve formě textového řetězce (datový typ string). Tento parametr bude představovat text vybízejícího uživatele, aby zadal reálnou hodnotu operandu. V metodě Main bude tato univerzální metoda využita jako pro zadání hodnoty operandu a, tak i operandu b. Deklarovaná metoda využívá výstupním parametr, který představuje validní hodnotu reálného čísla operandu. Nejprve jsou za pomoci příkazu ReadLine, načteny číslice (popřípadě desetinná čárka) zadané uživatelem. Tyto číslice jsou uloženy do lokální proměnné cisloText. Po tomto příkazu následuje test, zda je možné tyto číslice konvertovat do podoby reálného čísla. Tento test konverze je proveden za pomoci příkazu if, do jehož podmínky umístíme booleanovský výstup příkazu TryParse. Tento příkaz navrací informaci ve formě booleanovské hodnoty (true nebo false), zda je možné číslice načtené v textovém řetězci konvertovat na reálné číslo. Pokud ano, výstupem tohoto příkazu je hodnota true a zároveň je reálné číslo uloženo do výstupního parametru cislo. Pokud je výstupem metody false, vyvoláme výjimku. Objektu výjimky předáme text "Špatně zadané reálné číslo". 

//Metoda pro načtení validního operandu
static void Operand(string text, out double cislo)
{
    string cisloText;
    //Výpis výzvy se vstupním textem
    Console.Write(text);
    //Načtení prvního zadaného čísla
    cisloText = Console.ReadLine();
    /*Test zda dojde ke konverzi, pokud ano je výstupní parametr cislo, 
      pokud ne, je vyvolána výjimka*/ 
    if (double.TryParse(cisloText, out cislo) == false)
        //Vyvolání výjimky
        throw new FormatException("Špatně zadané reálné číslo");
}

Kód 4: Deklarace statické metody Operand – načtení validního operandu

U následující metody bez výstupu s názvem AritmetickaOperace vidíme, že metoda přijímá tři vstupní formální parametry. První parametr kod představuje číselný kód aritmetické operace, na jehož základě bude provedena aritmetická operace s dalšími dvěma vstupními parametry, představujícími oba operandy a a b. V metodě využíváme lokální proměnnou c datového typu double, která představuje výsledek aritmetické operace. Rozhodování, která operace má být provedena je pomocí vícenásobného větvení, tedy příkazu switch. Vzhledem k tomu, že jsme obdrželi validní hodnotu kódu operace z metody KodOperace (pokud by hodnota nebyla validní, nastala by výjimka), přesunuli jsme kód pro operaci dělení do větve default (pokud bude kód roven hodnotě 4, budou příkazy uvnitř větve default. Ostatní aritmetické operace jsou umístěny do předchozích větví). Po výpočtu výsledku uvnitř některé z větví, následuje jeho výpis včetně uvozujícího textu. Jak jsme již několikrát vysvětlili, při použití reálných čísel u obou operandů není zapotřebí ošetřovat  výjimku pro dělení nulou typu DivideByZeroException.

/*Metoda provádějící aritmetické operace na vstupních operandech podle 
  čísla kódu*/
static void AritmetickaOperace(int kod, double a, double b)
{
    //Deklarace lokální proměnné
    double c;
    //Potenciálně nebezpečný kód, který může způsobit výjimku
    switch (kod)
    {
        //Operace sečtení
        case 1:
            //Sečtení dvou zadaných čísel
            c = a + b;
            //Ukončení sekce pro kod == 1
            break;
        //Operace odečtení
        case 2:
            //Odečtení dvou zadaných čísel
            c = a - b;
            break;
        //Operace násobení
        case 3:
            //Násobení dvou zadaných čísel
            c = a * b;
            break;
        //Operace dělení
        default:
            //Podíl dvou zadaných čísel
            c = a / b;
            break;
    }
    //Výpis textu s hodnotou výsledku
    Console.WriteLine("Výsledek C: {0}", c);
}

Kód 1: Deklarace statické metody AritmetickaOperace – provedení aritmetické operace na základě kódu operace

V následujícím výpisu v konzoli vztahující se k našemu programu KalkulackaVyvolaniVyjimky je uveden příklad operace dělení čísla 5 a 2 – viz Výstup programu 1. Naše aplikace tedy funguje, pokud neuděláme nějakou chybu. To se ale děje jen v ideálním případě. Pojďme tedy funkčnost aplikace trochu otestovat.

Zadejte kód operace (1 ... sčítání, 2 ... odečítání, 3 ... násobení, 4 ... dělení): 4
Zadejte A: 5
Zadejte B: 2
Výsledek C: 2,5

Výstup programu 1 - Kód KalkulackaVyvolaniVyjimky_Main: Ukázka výsledku podílu

V následujícím výpisu v konzoli vidíme, co se stane, když uživatel namísto zadání celého čísla kódu operace zadá text „bflm“ – viz Výstup programu 2. Tento text se jen těžko dá konvertovat do podoby celého čísla. Proto byl vykonán handler s typem výjimky FormatException umístěný v metodě Main. Poněvadž jsme si definovali i objekt odvozený od této třídy výjimky, nechali jsme si vypsat text "Chyba při konverzi" včetně výpisu parametru handleru objektu exFormat typu FormatException: Input string was not in a correct format) atd.

Zadejte kód operace (1 ... sčítání, 2 ... odečítání, 3 ... násobení, 4 ... dělení): bflm
Chyba při konverzi: System.FormatException: Input string was not in a correct format.
   at System.Number.StringToNumber(ReadOnlySpan`1 str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
   at System.Number.ParseInt32(ReadOnlySpan`1 s, NumberStyles style, NumberFormatInfo info)
   at System.Int32.Parse(String s)
   at KalkulackaVyvolaniVyjimky.Program.KodOperace() in C:\Users\...\Programy\KalkulackaVyvolaniVyjimky\KalkulackaVyvolaniVyjimky\KalkulackaVyvolaniVyjimky\Program.cs:line 52
   at KalkulackaVyvolaniVyjimky.Program.Main(String[] args) in C:\Users\fox\...\Programy\KalkulackaVyvolaniVyjimky\KalkulackaVyvolaniVyjimky\KalkulackaVyvolaniVyjimky\Program.cs:line 16

Výstup programu 2 - Kód KalkulackaVyvolaniVyjimky_Main: Ukázka výsledku při chybné konverzi včetně parametru handleru

Ve výpisu v konzoli vidíme, co se stane, když uživatel namísto zadání povolené hodnoty číselného kódu aritmetické operace zadá nevalidní hodnotu, v našem případě 5. V metodě ValidaceKoduOperace jsme vyvolali výjimku typu ArgumentOutOfRangeException, kterou jsme následně ošetřili pomocí handleru v metodě Main - viz Výstup programu 3. Jak si můžeme všimnout, v následující řádce výpisu je parametr "Neplaný číselný kód operace" naší vyvolané výjimky - objektu, který jsme předali v metodě ValidaceKoduOperace.

Zadejte kód operace (1 ... sčítání, 2 ... odečítání, 3 ... násobení, 4 ... dělení): 5
Kód operace: System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: Neplaný číselný kód operace
   at KalkulackaVyvolaniVyjimky.Program.ValidaceKoduOperace(Int32 kodOperace) in C:\Users\fox\OneDrive\TI\TI_Nove\Prednasky\Podklady\Programy\KalkulackaVyvolaniVyjimky\KalkulackaVyvolaniVyjimky\KalkulackaVyvolaniVyjimky\Program.cs:line 64
   at KalkulackaVyvolaniVyjimky.Program.KodOperace() in C:\Users\fox\...\Programy\KalkulackaVyvolaniVyjimky\KalkulackaVyvolaniVyjimky\KalkulackaVyvolaniVyjimky\Program.cs:line 52
   at KalkulackaVyvolaniVyjimky.Program.Main(String[] args) in C:\Users\fox\...\Podklady\Programy\KalkulackaVyvolaniVyjimky\KalkulackaVyvolaniVyjimky\KalkulackaVyvolaniVyjimky\Program.cs:line 16

Výstup programu 3 - Kód KalkulackaVyvolaniVyjimky_Main: Ukázka výsledku při porušení rozsahu platných hodnot argumentu

Další výstup programu ukazuje, co se stane, pokud zadáme špatnou hodnotu některého z operandů. V metodě Operand je vyvolána výjimka typu FormatException a zároveň je metodě Main předán parametr "Špatně zadané reálné číslo", který nám vypsala v konzoli - viz  Výstup programu 4.

Zadejte kód operace (1 ... sčítání, 2 ... odečítání, 3 ... násobení, 4 ... dělení): 3
Zadejte A: bflm
Chyba při konverzi: System.FormatException: Špatně zadané reálné číslo
   at KalkulackaVyvolaniVyjimky.Program.Operand(String text, Double& cislo) in C:\Users\...\Podklady\Programy\KalkulackaVyvolaniVyjimky\KalkulackaVyvolaniVyjimky\KalkulackaVyvolaniVyjimky\Program.cs:line 79
   at KalkulackaVyvolaniVyjimky.Program.Main(String[] args) in C:\Users\...\Podklady\Programy\KalkulackaVyvolaniVyjimky\KalkulackaVyvolaniVyjimky\KalkulackaVyvolaniVyjimky\Program.cs:line 18

Výstup programu 4 - Kód KalkulackaVyvolaniVyjimky_Main: Ukázka výsledku při načtení špatné hodnoty operandu

Jak jsme již několikrát přesvědčili, při použití reálných čísel u obou operandů není zapotřebí ošetřovat výjimku pro dělení nulou. Výsledek bude nekonečno, který bude ve výpisu v konzoli zobrazen jako znak otazníku?. Pokud budeme vyhodnocovat výraz 0.0 / 0.0 C # nám vygeneruje hodnotu zvanou NaN, což znamená, že výsledek operace není číslem.