Zpracování chyb a výjimek
| Sitio: | Moodle ZČU |
| Curso: | KPV/TI Technická informatika ve strojírenství - šablona |
| Libro: | Zpracování chyb a výjimek |
| Imprimido por: | Nepřihlášený host |
| Día: | domingo, 8 de noviembre de 2026, 18:29 |
1. Zpracování chyb a výjimek
Občas při běhu programu dojde k chybě, kvůli které se celý běh programu může zhroutit. Takovým chybám se říká Runtime Error. Tyto chyby (chyby při běhu programu) se dělí na I/O chyby ( anglicky "Input / Output" – chyby na vstupu a na výstupu) a fatální chyby, které ukončí program. Zdrojů takových chyb je řada, zejména takových, které uživatel nezáměrně provede, např. dělení nulou, zápis do paměti, která není alokována, neexistence souboru, který chceme otevřít atd.
Při programování lze do programu implementovat kód, který se má provést, pokud dojde k chybě. Znamená to, že program přestane provádět následující instrukce a přesune se do části programu, kde jsou nadefinovány postupy, které mají být provedeny při tzv. výjimce (anglicky „Exception“). Optimisticky lze říci, že k těmto chybám by mělo docházet pouze výjimečně.
V aplikaci lze tedy detekovat různé typy chyb a podle typu chyb, lze tyto chyby ošetřit.
2. Obsluha výjimek
V kódu je vhodné předcházet chybám jiným způsobem, než pouhým konstatováním „Nastala chyba“. Můžeme do kódu přidávat vlastní kód pro detekci chyb kolem každého příkazu, což je ale nešikovné, zdlouhavé a může to vést k vyšší náchylnosti k chybám.
Takový případ jsme již zažili v různých modifikacích algoritmu Kalkulacka. Vraťme se ke zdrojovému kódu KalkulackaVicenasobneVetveni, který řešil 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 čísel a a b. Rozhodování o provedení aritmetické operace bylo provedeno pomocí vícenásobného větvení proměnné kod a výsledek aritmetické operace vypište v hlavním programu. Celý kód statické metody Main naleznete v následujícím odkazu:
-
Řešení: KalkulackaVicenasobneVetveni_Main
Podívejme se na jeden z potenciálně nebezpečných příkazů ve zdrojovém kódu, který by mohl vyvolat problémy - viz Kód 1. Představme si, že namísto očekávaného zadání celočíselného kódu aritmetické operace uživatel zadá text "bflm".
//Deklarace proměnných: 'kod' ... celočíselný kód aritmetické operace int kod; // ... //Načtení kódu operace kod = int.Parse(Console.ReadLine()); // ...
Kód 1: Výřez zdrojového kódu - program KalkulackaVicenasobneVetveni_Main - potenciálně nebezpečný kód konverze textu na celé číslo
Pokud jste provedli zadání textu "bflm" namísto celého čísla, byla vyvolána tzv. neošetřená výjimka - viz Obrázek 1.

Obrázek 1: Neošetřená výjimka vstupního řetězce
Výjimka tedy představuje specifický typ chyby, který může být vyvolán (nastat) při provádění programu. V kódu je tedy na snaze zpracovat ošetření možné chyby, tzv. obsluhy výjimky (anglicky „Exception“). Ošetření potenciálních chyb je možné pomocí příkazů try a catch.
Obecná syntaxe takových příkazů je:
try
{
nebezpečnýPříkaz
}
catch
{
těloHandleru
}
kde:
- nebezpečnýPříkaz - představuje příkaz nebo složený příkaz, který obsahuje potenciální nebezpečí chyby. Když dojde ke spuštění tohoto bloku a nedojde k vyvolání výjimky (nedojde k žádné chybě), proběhnou všechny příkazy postupně
- těloHandleru - pokud dojde k chybě při provádění nebezpečného příkazu, provádění kódu v tomto nebezpečném příkazu se opustí a skočí se do bloku umístěného za klíčové slovo catch, tzn., že bude provedeno tělo tzv. handleru. Handler (v překladu obsluha) představuje část kódu, který může ošetřit výjimku.
U handleru lze postupovat následujícími způsoby:
- Uvést parametr handleru:
catch(typVýjimky identifikátorVýjimky) {těloHandleru}
- Neuvádět parametr handleru:
catch{těloHandleru}
kde:
- typVýjimky - je výjimka vyjádřena jako parametr handleru, tzn. je uveden typVýjimky a za ním je uveden objekt třídy výjimky – identifikátorVýjimky. Tento objekt v sobě nese podrobné informace o výjimce
- těloHandleru - je kód, kterým bude ošetřen daný typ výjimky. Můžeme mít specifikovány různé kódy pro obsluhu různých typů výjimek, ke kterým může dojít v bloku za příkazem try. Pokud tedy některý z příkazů v bloku za try způsobí chybu, běhové prostředí (anglicky „Runtime“ – když je prováděn program) .NET Framework vygeneruje a vyvolá výjimku. Toto běhové prostředí prozkoumá typ vyvolané výjimky a je proveden kód obsluhující danou výjimku.
Druhou možností je neuvádět parametr handleru, tzn. neuvádět kulatou závorku a v ní typ a identifikátor výjimky. Tato možnost představuje univerzální handler, který zachytí všechny výjimky bez rozdílu. Tento způsob obsluhy se však nedoporučuje, poněvadž absencí objektu generovaného podle určité třídy výjimky ztrácíme informace o příslušné chybě.
Když obslužný kód výjimky skončí, program pokračuje prvním příkazem za tímto obslužným.
Existují různé typy výjimek. Jednotlivé typy výjimek (rozdělené podle třídy) např. objekt vygenerovaný podle třídy FormatException má v sobě uloženy různé informace, pomocí nichž můžeme prozkoumat přesnou příčinu výjimky. Mnoho z těchto vlastností je společných u všech typů výjimek např. vlastnost Message obsahuje textový popis chyby, která vyvolala výjimku. Tato vlastnost může být využita např. pro zápis do souboru se záznamem (s příponou *.log) činnosti aplikace, nebo ji lze zobrazit uživateli.
Běží-li program v režimu ladění ve Visual Studio můžeme (anglicky „Debugging“) výjimky generované naší aplikací zkoumat. Ladící program převezme řízení a pomocí dialogového okna lze zjistit příčinu vzniku výjimky. Lze i měnit hodnoty proměnných, krokovat kód atd.
Ilustrativní příklad KalkulackaVyjimka
Program KalkulackaVyjimka
- Popis: Ošetření výjimky chyby na vstupu
- Zadání: Zkuste implementovat v hlavní statické metodě Main pomocí příkazů try, catch ošetření výjimky vyvolané nesprávným zadáním celočíselného kódu aritmetické operace v programu KalkulackaVicenasobneVetveni_Main. Namísto celého čísla bude zadán text "bflm". V případě, že nebude zadán validní kód aritmetické operace, hlavní metodu ukončete
- Řešení:
KalkulackaVyjimka_Main
Ve výřezu našeho zdrojového kódu týkajícího se programu KalkulackaVyjimka vidíme, že jsme do kódu před potenciálně nebezpečný příkaz načtení celočíselného kódu aritmetické operace umístli příkaz try včetně začátku složené závorky – viz Kód 2. Konec složené závorky umístíme za příkazy kod = int.Parse(Console.ReadLine()); ,tj příkaz který načte vstupní údaj zadaný uživatelem, dále ho konvertuje na celé číslo a následně ho uloží do proměnné kod. Za složené závorky příkazu try umístíme handler catch, tzn., jaké nápravné opatření (jaké příkazy) se mají v případě chyby provést. Handler ošetřuje výjimku typu FormatException - hodnota není ve vhodném formátu, který má být převeden z řetězce pomocí konverze, jako je např. příkaz Parse. Při výjimce je také vygenerován objekt exFormat, který v sobě informace o dané chybě. Tento objekt následně využijeme pro výpis chyby exFormat.ToString(), která nastala. Za příkaz výpisu umístíme příkaz ReadKey, abychom si mohli informace o chybě přečíst. Příkazem return ukončíme metodu, protože nastala chyba a nelze určit aritmetickou operaci.
// ... //Potenciálně nebezpečný kód try { //Načtení kódu operace kod = int.Parse(Console.ReadLine()); } //Handler chyby při konverzi s parametrem handleru catch (FormatException exFormat) { //Výpis parametru handleru Console.WriteLine("Chyba při konverzi: {0}", exFormat.ToString()); //Čekání na stisk klávesy -> Ukončení programu Console.ReadKey(); return; } // ...
Kód 2: Výřez kódu ošetření výjimky chyby na vstupu u programu KalkulackaVicenasobneVetveni
Další možností, jak zjistit zda je možné číslice konvertovat do podoby celého čísla je využít příkazu int.TryParse. Tento test konverze je obvykle proveden za pomoci příkazu if, do jehož podmínky umístíme booleanovský výstup příkazu int.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 celé číslo. Pokud ano, výstupem tohoto příkazu je hodnota true a zároveň je celé číslo uloženo do výstupního parametru. Pokud je výstupem metody false, nelze číslice konvertovat.
V následujícím výstupu programu můžeme vidět, jak se program zachová, pokud uživatel zadal špatný vstupní údaj týkající se číselného kódu aritmetické operace. Nastane výjimka a provede se výpis o chybě, která nastala - viz Výstup programu - Kód KalkulackaVyjimka_Main.
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: Vstupní řetězec nemá správný formát.
v System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
v System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
v System.Int32.Parse(String s)
v KalkulackaVyjimka.Program.Main(String[] args) v C:\Users\fox\OneDrive\TI\TI_Nove\Prednasky\Podklady\Programy\Kalkulacka_nove\KalkulackaVyjimka\KalkulackaVyjimka\KalkulackaVyjimka\Program.cs:řádek 25
Výstup programu - Kód KalkulackaVyjimka_Main
3. Neošetřené výjimky
Pokud se stane, že neošetříme možnou chybu v těle po příkazu try uvnitř metody, tzn., že nastane taková výjimka, pro kterou nebude definován příslušný handler s catch, nastane ukončení této metody, v níž nastala chyba.
Pokud by stejný případ nastal u volané metody, která je umístěna v bloku try volající metody a ve volané metodě by došlo k chybě a volaná metoda by neobsahovala příslušný handler, je řízení přepnuto zpět do volající metody. Pokud i volající metoda neobsahuje příslušný handler výjimky, je metoda ukončena a následně je ukončen i celý program v důsledku neošetřené výjimky.
Pokud by tato volající metoda byla volána z jiné metody, je tato jiná metoda prozkoumána, zda neobsahuje odpovídající handler (metody jsou kaskádově prozkoumávány v rámci nalezení příslušného handleru). Pokud je nalezen odpovídající handler s catch, je spuštěn kód v tomto handleru a kód pokračuje prvním příkazem za tělem handleru, který danou výjimku zachytil. [6]
Ilustrativní příklad Vyjimky
Program Vyjimky
- Popis: Princip hledání příslušného handleru při kaskádovém volání metod (absence výjimky ve volané metodě)
- Zadání: Navrhněte program, který v hlavní statické metodě Main v bloku příkazu catch volá metodu s celočíselným návratem s názvem Metoda1. V Main vytvořte handler, který obsluhuje výjimku typu FormatException, která je vyvolána v případě chyby při konverzi. Samotné zadání celého čísla proběhne v jiné statické metodě s návratem nazvané jako Metoda2. V této metodě však chybí příslušný handler pro ošetření chyby na vstupu (při konverzi). V metodě Metoda2 je zadáno celé číslo a toto číslo je zároveň jejím návratem. Aby byl zajištěn princip kaskádování, v těle Metoda1 zavolejte statickou metodu s celočíselným návratem Metoda2 (metoda Main volá metodu Metoda1, která volá metodu Metoda2). Abychom věděli, ve které části programu se nacházíme, do hlavní metody Main do příkazu catch umístěte výpis textu "Handler v metodě Main". V Main za příkaz catch pak umístěte výpis textu "Příkaz za handlerem v metodě Main". Do metody Metoda2 umístěte za načtení čísla výpis "Handler v metodě Metoda2"
- Řešení: Bude postupně probráno v následujícím textu
Následující příklad ilustruje podstatu kaskádového volání jednotlivých metod a absence dané výjimky u poslední
volané metody, kde by potenciálně mohla nastat chyba. Vše začíná voláním statické metody nazvané Metoda1, která vrací obdržený výsledek do
statické hlavní metody Main - viz Kód 1.
Tato metoda Metoda1 ve svém těle volá další statickou
metodu Metoda2 - viz Kód 2.
static void Main(string[] args) { //Deklarace celočíselné proměnné int vysledek; //Hlídaný příkaz try { //Návrat výsledku metody Metoda1 do proměnné vysledek vysledek = Metoda1(); } //Handler - obsluha výjimky vyvolané chybou při konverzi catch (FormatException e) { //Výpis textu Console.WriteLine("Handler v metodě Main"); } //Výpis textu Console.Write("Příkaz za handlerem v metodě Main"); //Čekání na stisk klávesy -> ukončení programu Console.ReadKey(); }
Kód 1: Deklarace hlavní statické metody Main - obsahuje handler pro ošetření vyvolané výjimky v jiné metodě
V těle metody Metoda1 vidíme, že Metoda2 vrací celočíselnou načtenou hodnotu do volající metody Metoda1 - viz Kód 2.
//Deklarace volané metody z metody Main static int Metoda1() { //Návrat výsledku z voláné metody Metoda2 return Metoda2(); }
Kód 1: Deklarace statické metody Metoda1 - volání metody Metoda2
Výsledek obdržený z Metoda2 představuje celé číslo, získané konverzí textového řetězce zadaného uživatelem na klávesnici - viz Kód 3. To vše by bylo v pořádku až do té doby, kdy uživatel namísto textu představujícím číslice zadá text např. „bflm“. V tu dobu nastane chyba při konverzi u příkazu int.Parse, protože text nelze konvertovat na celé číslo. Dalším problém je, že v Metoda2 chybí odpovídající handler pro ošetření chyby na vstupu typu FormatException. Tento handler jsme záměrně umístili do metody Main, která provedla volání metody jako první (metoda Main volá metodu Metoda1, která volá metodu Metoda2). Pokud tedy platí princip uvedený v předchozím textu, mělo by být provedeno ošetření výjimky v metodě, která jako první nejbližší obsahuje daný handler, tedy metoda Main.
//Deklarace volané metody z metody Metoda1 static int Metoda2() { //Deklarace proměnné int x; //Výpis pro zadání Console.Write("Zadejte celé číslo: "); /*Konverze načtené textového řetezce od uživatele a uložení do proměnné x*/ x = int.Parse(Console.ReadLine()); //Výpis textu Console.WriteLine("Handler v metodě Metoda2"); //Návrat výsledku do volající metody return x; }
Kód 1: Deklarace statické metody Metoda2 - absence handleru pro ošetření výjimky
Dokážete si tipnout, jaký bude výpis v konzoli? Záměrně jsme do metod ještě umístili výpis, abychom poznali, jaký kód se bude po ošetření výjimky provádět. Výsledek je zobrazen na následujícím výpisu – viz Výstup programu - Kód 1. Na výpisu v konzoli vidíme, že příkaz výpisu v metodě Metoda2 se vůbec neprovedl, ale provedl se pouze kód za příslušným handlerem nalezeným v metodě Main.
Zadejte celé číslo: bflm Handler v metodě Main Příkaz za handlerem v metodě Main
Výstup
programu - Kód 1: Ukázka výsledku u hledání příslušného handleru
4. Ošetření více výjimek
V předchozích kapitolách bylo naznačeno, že existuje více druhů výjimek (tzn. více druhů chyb, které mohou nastat při provádění programu. Proto v C# lze definovat více handlerů pro specifické typy výjimek. Syntaxe použití více handlerů např. 2 handlery je následující:
try
{
nebezpečnýPříkaz
}
catch(typ1 identifikátorVýjimky1)
{
těloHandleru1
}
catch(typ2 identifikátorVýjimky2)
{
těloHandleru2
} kde:
- nebezpečnýPříkaz - představuje příkaz nebo složený příkaz, který obsahuje potenciální nebezpečí chyby. Když dojde ke spuštění tohoto bloku a nedojde k vyvolání výjimky (nedojde k žádné chybě), proběhnou všechny příkazy postupně
- těloHandleru1 - pokud dojde k chybě při provádění nebezpečného příkazu, provádění kódu v tomto nebezpečném příkazu se opustí a skočí se do příslušného bloku catch na základě typu výjimky. Pokud tedy nastala výjimka typu typ1 je generován objekt identifikátorVýjimky1 a výjimka bude ošetřena pomocí těloHandleru1
Pokud kód v prvním handleru vygeneroval další výjimku, nebude hledán příslušný handler v sousedních handlerech dané metody, ve které nastala výjimka (i když tam je příslušný handler definován), ale bude hledán příslušný handler ve volající metodě (kaskádování) – princip, který jsme ukazovali již v předchozí kapitole Neošetřené výjimky.
Mechanismus ošetření výjimek v C# je velmi komplexní a zachycuje většinu možných výjimek, které mohou nastat. Jak ale zajistit ošetření všech možných výjimek, které jsme ani při psaní kódu neuvažovali? Výjimky jsou ve vzájemných vztazích. Výjimky jsou organizovány do rodin nazývajících se hierarchie dědičnosti např. FormatException a OverflowException patří do rodiny zvané SystemException, stejně jako řada dalších výjimek. Výjimka SystemException je sama členem širší rodiny jednoduše nazvané Exception, která je pradědečkem všech výjimek. Pokud tedy ošetříme výjimku typu Exception, zachytíme tím všechny možné výjimky, které mohou být vyvolány. Rodina výjimek zahrnuje celou řadu výjimek, z nichž mnohé jsou určeny pro použití různými částmi rozhraní .NET Framework. Některé z těchto výjimek jsou poněkud ezoterické, ale je užitečné pochopit, jak je ošetřit. [6]
Asi si říkáte, co se stane, když výjimka bude vyhovovat dvou handlerům např. pro výjimku typu FormatException a obecný typ Exception. V tomto případě bude spuštěn první nalezený handler, který vyhovuje typu vyvolané výjimky. Všechny ostatní handlery budou ignorovány. Pokud tedy na začátek obsluh výjimek specifikujeme obecnou výjimku typu Exception ostatní obsluha výjimek nebude spuštěna. Proto je vhodné na začátek bloků s catch umísťovat specifické handlery a za nimi ty obecnější.
Ilustrativní příklad KalkulackaTypyVyjimek
Program KalkulackaTypyVyjimek
- Popis: Ošetření více typů výjimek
- Zadání: Zkuste v hlavní statické metodě Main v programu KalkulackaVicenasobneVetveni_Main implementovat pomocí příkazů try, catch ošetření různých typů výjimek. Aplikujte ošetření try na potřebný zdrojový kód. Dané výjimky otestujte. Jedná se o následující typy výjimek:
- DivideByZeroException - dělení nulou
- FormatException - chyba při konverzi
- Exception - obecná výjimka
- Řešení:
KalkulackaTypyVyjimek_Main
Vraťme se k našemu
příkladu kalkulačky. Pro vyzkoušení obsluhy výjimek použijeme verzi programu KalkulackaVicenasobneVetveni_Main s vícenásobným
větvením. Potenciálně nebezpečný kód jsme zavřeli do těla příkazu try – viz Kód 1. Prvním
problémem, který může nastat, je chyba na vstupu, to znamená, že uživatel zadá
namísto číslice nějaká písmena nebo jiné znaky. Pak by došlo k chybě při
konverzi načteného textu do podoby čísla, která by vyvolala výjimku FormatException. Tento problém může
nastat u příkazů Parse u typu int a double.
static void Main(string[] args) { /*Deklarace proměnných: 'kod' ... celočíselný kód aritmetické operace, * 'a' ... první zadané číslo, 'b' ... druhé zadané číslo; * 'c' ... výsledek aritmetické operace*/ int kod; double a, b, c; //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í): "); //Potenciálně nebezpečný kód try { //Načtení kódu operace kod = int.Parse(Console.ReadLine()); //Výpis výzvy k zadání prvního čísla Console.Write("Zadejte A: "); //Načtení prvního zadaného čísla a = double.Parse(Console.ReadLine()); //Výpis výzvy k zadání druhého čísla Console.Write("Zadejte B: "); //Načtení druhého zadaného čísla b = double.Parse(Console.ReadLine()); //Přepnutí do příslušné sekce podle hodnoty 'kod' switch (kod) { //Operace sečtení case 1: //Sečtení dvou zadaných čísel c = a + b; //Ukonnč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í case 4: //Podíl dvou zadaných čísel c = a / b; break; //Zadání špatného číselného kódu operace default: //Výpis chyby v zadávání kódu Console.WriteLine("Zadali jste špatný číselný kód!"); //Zamezení provádění dalších příkazů - opuštění metody return; } //Výpis textu s hodnotou výsledku Console.WriteLine("Výsledek C: {0}", c); }
Kód 1: První výřez kódu těla hlavní metody KalkulackaTypyVyjimek_Main – použití příkazu try
Druhá část kódu obsahuje různé handlery (bloky s obsluhou výjimek pomocí příkazů catch ať už s typem výjimky a objektem, nebo bez). Tyto handlery následují po ukončení těla příkazu try (za koncem složené závorky) – viz Kód 2. Je zde uvedeno ošetření možných vyvolaných výjimek – pro případy:
- Dělení nulou – typ výjimky DivideByZeroException
- Chyby při konverzi textového řetězce na číslo, ať už celé číslo (u proměnné kod), tak na reálné číslo (proměnná a, b) pomocí příkazu Parse – typ výjimky FormatException
- Nebo jiné výjimky, které mohou nastat – typ výjimky Exception. S použitím této výjimky buďte opatrní. Je to prapředek všech výjimek, takže jakákoliv jiná výjimka, která nebyla uvedena před touto obecnou (vyjma předchozích dvou), bude spadat do tohoto handleru. U tohoto handleru nemusí být uvedena ani kulatá závorka s typem a identifikátorem výjimky. Stačí pouze napsat catch a tělo bloku kódu ošetřujícího výjimku. Tím ale bohužel zároveň ztrácíme informaci o příčině chyby
//Handler pro dělení nulou bez parametru handleru catch (DivideByZeroException) { //Výpis chyby Console.WriteLine("B nesmí být 0"); //Zamezení provádění dalších příkazů - opuštění metody return; } //Handler chyby při konverzi s parametrem handleru catch (FormatException exFormat) { //Výpis parametru handleru Console.WriteLine("Chyba při konverzi: {0}", exFormat.ToString()); } //Obecná výjimka typu Exception (stačilo by pouze catch) – používat obezřetně catch (Exception ex) { Console.WriteLine("Nastala jiná výjimka: {0}", ex.ToString()); } //Čekání na stisk klávesy -> Ukončení programu Console.ReadKey(); }
Kód 2: Druhá část kódu těla hlavní metody KalkulackaTypyVyjimek_Main – použití různých typů výjimek
V následujícím výpisu
v konzoli vztahující se k našemu programu Kalkulacka 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 KalkulackaTypyVyjimek_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 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.
Poněvadž jsme si definovali i objekt odvozený od této třídy výjimky, máme
možnost si nechat vypsat (pomocí příkazu ToString())
příčinu selhání našeho programu. Z vypsaného textu chyby lze zjistit, vstupní
textový řetězec nebyl ve správném formátu (v případě, že jste si nainstalovali českou sadu ve Visual Studio: Vstupní řetězec nemá správný formát . Pokud máte anglickou lokaci výpis výjimky vypadá asi takto: 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í): 4
Zadejte A: bflm
Chyba při konverzi: System.FormatException: Vstupní řetězec nemá správný formát.
v System.Number.ParseDouble(String value, NumberStyles options, NumberFormatInfo numfmt)
v System.Double.Parse(String s)
v KalkulackaTypyVyjimek.Program.Main(String[] args) v C:\Users\...\Programy\Kalkulacka_nove\KalkulackaTypyVyjimek\KalkulackaTypyVyjimek\KalkulackaTypyVyjimek\Program.cs:řádek 29
Výstup programu 2 - Kód KalkulackaTypyVyjimek_Main: Ukázka
výsledku při chybné konverzi včetně popisu chyby
Dokážete si tipnout, jaký bude výpis v konzoli, pokud bychom zadali operaci dělení a jako jmenovatele jsme uvedli 0? Ve zdrojovém kódu vidíte, že je uvedena výjimka pro dělení nulou DivideByZeroException. Pokud se ale pozorně kouknete do výpisu v konzoli, žádný výpis chyby dělení nulou nenaleznete - viz Výstup programu 3. Jak je to možné? Datové typy proměnných jsou double. Pokud tedy přepíšeme operandy výrazu na reálná čísla, kterým skutečně jsou, tzn. 5.0 / 0.0, výsledek bude nekonečno (anglicky „Infinity“). Ve výpisu v konzoli vidíme nekonečno jako znak otazníku?. O této záludnosti jsme se již bavili v kapitole Operátory. Výsledek je zobrazen na následujícím výpisu.
Zadejte kód operace (1 ... sčítání, 2 ... odečítání, 3 ... násobení, 4 ... dělení): 4
Zadejte A: 5
Zadejte B: 0
Výsledek C: ?
Výstup programu 3 - Kód KalkulackaTypyVyjimek_Main: Ukázka výsledku nekonečno
Jaká by tedy byla hodnota výrazu 0.0 / 0.0? Obvykle, pokud vydělíte nulu nějakou hodnotou, výsledek je nula. Pokud ale vydělíte číslo nulou, výsledkem je nekonečno. Výraz 0.0 / 0.0 vede k paradoxu; hodnota musí být současně nula a nekonečno. C # má pro tuto situaci další zvláštní hodnotu zvanou NaN, což znamená, že není číslem (zkratka anglického „Not a Number“). Pokud tedy vyhodnocujete výraz 0.0 / 0.0, výsledkem je NaN. Výsledek je zobrazen na následujícím výpisu – viz Výstup programu 4.
Zadejte kód operace (1 ... sčítání, 2 ... odečítání, 3 ... násobení, 4 ... dělení): 4
Zadejte A: 0
Zadejte B: 0
Výsledek C: NaN
Výstup programu 4 - Kód KalkulackaTypyVyjimek_Main: Ukázka výsledku Not a Number
Uveďme vyhodnocení výrazu např. 5/0, pokud by oba dva operandy a, b byly deklarovány jako datový typ int. Vyhodnocení výrazu způsobí chybu a nastala by výjimka typu DivideByZeroException. V těle tohoto handleru by byla vypsána hláška, která je zobrazena ve výpisu v konzoli – viz Výstup programu 5.
Zadejte kód operace (1 ... sčítání, 2 ... odečítání, 3 ... násobení, 4 ... dělení): 4
Zadejte A: 5
Zadejte B: 0
B nesmí být 0
Výstup programu 5 - Kód KalkulackaTypyVyjimek_Main: Výjimka
typu DivideByZeroException
5. Filtrování výjimek
Výjimky se také dají filtrovat, abychom zajistili, že obsluha výjimky proběhne tehdy, pokud jsou splněny další podmínky. Takové podmínky booleanovského typu lze definovat za klíčovým slovem when. Obecná syntaxe uvedené podmínky může vypadat následovně:
bool obsluhaVyjimky = …;
try
{
nebezpečnýKód
}
catch(typ identifikátorVýjimky) when(obsluhaVyjimky == true)
{
těloHandleru
}
kde:
- obsluhaVyjimky - booleanovská proměnná, která určuje zda má být provedeno ošetření výjimky, tj., proměnná nabývá hodnoty true. Pokud je hodnota proměnné false, kód v těle handleru, který má být proveden v případě, že je vyvolána výjimka, není proveden a použije se výchozí mechanismus zpracování výjimek pro aplikaci
Pokud více metod používá stejné ošetření výjimek, je vhodné volání metod umístit do těla příkazu try a za toto tělo umístit příkaz catch s ošetřením dané výjimky pro volané metody, aby se kód pro ošetření stejné výjimky neduplikoval.
6. Přetečení
Datové typy mají své rozsahy hodnot. V kódu můžeme využít vlastnost MaxValue, která nám vrátí horní mez rozsahu u daného datového typu. Může se stát, že vykonáním aritmetické operace překročíme povolený rozsah. Dojde k tzv. přetečení (anglicky "Overflow").
Pokud se chceme v C# pojistit, aby nás kompilátor upozornil, že během početních operací prováděných na celých číslech došlo k přetečení, můžeme využít klíčové slovo checked. Toto slovo nám zajistí, že v případě přetečení budeme upozorněni kompilátorem na tuto nepříjemnou skutečnost. Může se to zdát divné, ale existuje i protipól – klíčové slovo unchecked. To slovo potlačuje hlášení o přetečení a je využíváno záměrně při některých matematických výpočtech.
Pokud je ale uzavřeno do složeného příkazu za
příkazem checked volání metody, kontrola přetečení se na tento kód uvnitř
volané metody nevztahuje.
Ilustrativní příklad Preteceni
Program Preteceni
- Popis: Přetečení datového typu integer
- Zadání: Vytvořte program pro konzolovou aplikaci, ve kterém v hlavní statické metodě Main deklarujete celočíselnou proměnnou horniMez a uložíte do ní největší možné maximální kladné číslo. Hodnotu této proměnné vypište. V následném kroku k této hodnotě přičtěte hodnotu 1 a nechte znovu vypsat hodnotu v proměnné
- Řešení: Bude postupně probráno v následujícím textu
Následující kód ukazuje, jak
využijeme vlastnosti určení maximální hodnoty určitého datového typu. Do proměnné horniMez
uložíme tuto hodnotu celočíselného datového typu int – viz Kód 1.
Abychom se dozvěděli aktuální hodnotu proměnné, vypíšeme aktuální hodnotu
uložené v proměnné pomocí příkazu Console.WriteLine.
Abychom dosáhli přetečení (tedy
překročení rozsahu daného datového typu), k aktuální hodnotě v proměnné
přičteme hodnotu 1. Tento výsledek necháme znovu vypsat.
static void Main(string[] args) { //Deklarace celočíselné proměnné int horniMez; //Přiřazení hodnoty horní meze int (hodnota 2147483647) do proměnné horniMez = int.MaxValue; //Výpis akutální hodnoty proměnné Console.WriteLine("Horní mez: {0}", horniMez); //Přičtení 1 k aktuální hodnotě v proměnné -> přetečení horniMez = horniMez + 1; //Výpis akutální hodnoty proměnné Console.WriteLine("Horní mez + 1: {0}", horniMez); //Čekání na stisk klávesy -> Ukončení programu Console.ReadKey(); }
Kód 1: Deklarace hlavní metody Main programu Preteceni - přetečení hodnoty
Na následujícím výpisu programu vidíme, že nejprve byl proveden výpis aktuální hodnoty (horní mez datového typu int, tj. 2147483647) uložené v proměnné horniMez – viz Výstup programu - Kód 1. Co se ale stane, pokud jsme k této hodnotě přičetli 1? Jak vidíte, hodnota se nezvýšila na hodnotu 2147483648, ale spadla na dolní mez datového typu int, tj. -2147483648.
Horní mez: 2147483647
Horní mez + 1: -2147483648
Výstup programu - Kód 1: Ukázka
výsledku při přetečení
Ilustrativní příklad KontrolaPreteceni
Program KontrolaPreteceni
- Popis: Kontrola přetečení datového typu integer
- Zadání: Modifikujte program Preteceni (viz Kód 1) pro konzolovou aplikaci tak, aby nás kompilátor upozornil na přetečení celočíselné proměnné horniMez
- Řešení: Bude postupně probráno v následujícím textu
V následujícím zdrojovém kódu jsme využili klíčového slova checked, abychom si pojistili proti neošetřené výjimce typu OverflowException - přetečení - viz Kód 2. Nebezpečný kód, který je kontrolován horniMez = horniMez + 1;, jsme uzavřeli do složených závorek.
static void Main(string[] args) { //Deklarace celočíselné proměnné int horniMez; //Přiřazení hodnoty horní meze int (hodnota 2147483647) do proměnné horniMez = int.MaxValue; //Výpis akutální hodnoty proměnné Console.WriteLine("Horní mez: {0}", horniMez); //Kontrola přetečení checked { //Přičtení 1 k aktuální hodnotě v proměnné -> přetečení horniMez = horniMez + 1; } //Výpis akutální hodnoty proměnné Console.WriteLine("Horní mez + 1: {0}", horniMez); //Čekání na stisk klávesy -> Ukončení programu Console.ReadKey(); }
Kód 1: Deklarace metody KontrolaPreteceni - Příklad
přetečení s použitím checked
Když jsme spustili program, po výpisu horní meze celého čísla jsme byli upozorněni kompilátorem na nepříjemnou skutečnost přetečení prostřednictvím okna s neošetřenou výjimkou - viz Obrázek 1.

Obrázek 1: Informace kompilátoru u přetečení
Příkaz checked nebo unchecked lze použít pouze u aritmetických operací prováděných s celými čísly. V naší aplikaci týkající se algoritmu Kalkulacka, bychom mohli využít checked pouze tehdy, pokud bychom používali pouze celočíselné proměnné. Použití u reálných čísel by nevyvolalo výjimku typu OverflowException, o čemž jsme se přesvědčili již v kapitole Ošetření více výjimek, kde jsme narazili na pojem Infinity (nekonečno se znakem ?).
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.
8. Blok finally
Při použití příkazů try a catch se mění průběh provádění programu a nemusí být zaručeno spuštění příkazů, které jsou v programu důležité. Uveďme příklad čtení ze souboru. Pokud by nastala chyba při čtení, aplikace by spadla a nebylo by provedeno zavření souboru (soubor by byl stále otevřen). Abychom zamezili přeskočení důležitého kódu, lze využít příkazy finally. Obecná syntaxe příkazu finally:
finally
{
//Kód, který musí být vždy proveden
}
Tento příkaz zajistí, aby příkazy uzavřené v bloku příkazu finally byly vykonány bez rozdílu, zda byla vyvolána výjimka či nikoliv. Tento blok se umísťuje bezprostředně za blok try, nebo za poslední blok catch. Pokud program začne provádět blok try s přidruženým blokem finally, bude obsah bloku finally proveden vždy, i když dojde k výjimce. Jestliže je výjimka zachycena a zpracována lokálně (uvnitř aktuální metody), je nejprve provedena obsluha výjimky catch a poté blok finally. Jestliže je výjimka zpracována mimo aktuální metodu (je nutné prohledat seznam volajících metod), je nejdříve spuštěn blok finally. Blok finally bude proveden vždy. [9]
9. Třídy výjimek
Existují různé typy výjimek, které mají obecného předka třídu System.Exception. V následující tabulce jsou uvedeny základní třídy výjimek (pro .NET Framework 4.8) - viz Tabulka 1.
Nejčastěji používané typy výjimek jsou označeny tučným písmem.
|
Jméno třídy výjimky |
Popis |
|
ArgumentException |
Neplatný argument předaný metodě (není prázdný, tj. není nulovatelný typ – není null) |
|
ArgumentNullException |
Argument předaný metodě je nulovatelný |
|
ArgumentOutOfRangeException |
Argument je mimo rozsah platných hodnot |
|
DirectoryNotFoundException |
Část cesty k adresáři není platná |
|
DivideByZeroException |
Jmenovatel v celočíselném nebo desetinném dělení je nula |
|
DriveNotFoundException |
Jednotka není k dispozici nebo neexistuje |
|
FileNotFoundException |
Soubor neexistuje |
|
FormatException |
Hodnota není ve vhodném formátu, který má být převeden z řetězce pomocí konverze, jako je např. příkaz Parse |
|
IndexOutOfRangeException |
Index je mimo hranice pole nebo kolekce. |
|
InvalidOperationException |
Volání metody je v aktuálním stavu objektu neplatné |
|
KeyNotFoundException |
Zadaný klíč pro přístup k členu v kolekci nebyl nalezen |
|
NotImplementedException |
Metoda nebo operace není implementována |
|
NotSupportedException |
Metoda nebo operace není podporována |
|
ObjectDisposedException |
Operace se provádí na objektu, který již zanikl |
|
OverflowException |
Aritmetická operace, přetypování, nebo konverze vede k přetečení |
|
PathTooLongException |
Cesta, nebo název souboru přesahuje maximální délku definovanou systémem |
|
PlatformNotSupportedException |
Operace není na aktuální platformě (operačním systému) podporována |
|
RankException |
Metodě je předána matice s nesprávným počtem rozměrů (dimenzí) |
|
TimeoutException |
Časový interval přidělený operaci vypršel |
|
UriFormatException |
Byl použit neplatný identifikátor URI (zkratka anglického „Uniform Resource Identifier") |
Tabulka 1: Třídy výjimek [10]
10. Stručné shrnutí
| Pro ... | ... proveďte následující |
|---|---|
| Zachycení určitého typu výjimky | Uveďte blok try příkazy, které mají být vykonány. Za blokem try uveďte obslužnou rutinu catch, která zachytí daný typ výjimky a v bloku uveďte, jak má být výjimka ošetřena, např.:try { vek = int.Parse("2x2"); ... } catch (FormatException exFormat) { Console.WriteLine("Chyba při konverzi: {0}", exFormat.ToString()); ... } |
| Zajištění kontroly proti přetečení u celočíselných výpočtů | Použijte klíčové slovo checked, např.:promenna = int.MaxValue; checked { promenna++; } |
| Vyvolání výjimky | Použijte klíčové slovo throw, pak vytvořte objekt výjimky pomocí klíčového slova new a určete typ výjimky, např.:if (int.TryParse("2x2", out vek) == false) { throw new FormatException("Neproběhla konverze textu na celé číslo"); } |
| Zachycení všech výjimek v jedné rutině catch | Uveďte obslužnou rutinu catch, která zachytává výjimku obecného typu Exception:try { ... } catch (Exception ex) { ... } |
| Zajištění, aby určitý kód byl vždy proveden, i když před ním dojde k výjimce | Uveďte kód do bloku za klíčovým slovem finally:try { ... } finally { ... //vždy proběhne } |