# Výjimky a validace

### Úvod

V této kapitole si řekneme, jak v C# ošetřovat nesprávné vstupy od uživatele a jak pracovat s výjimkami. Pro tyto účely nám poslouží třída `Exception`.

### K čemu slouží výjimka

Výjimky slouží k ošetření krajních situací při běhu programu a případnému ukončení programu alespoň s vypsáním chybové hlášky. Jejich používání by se tedy nemělo přehánět. Obecně platí, že pokud náš kód vyvolává fatální výjimky při běžném používání, je správně jej přepsat tak, aby i v krajních situacích fungoval alespoň na základní úrovni a nebylo nutné jej ukončit. Správné použití výjimek by mělo být spíše pro debuggovací (tzn. testovací) účely. Pokud program poskytneme uživatelům, měla by se výjimka objevit jen tehdy, když nastane situace, kterou jako programátoři nemůžeme ovlivnit. Mezi takovéto situace můžeme řadit například absenci administrátorských práv, absence důležitých balíčků .NET frameworku a tak dále.

**\# Výjimka jako objekt**

Jak jsme si již v předchozích kapitolách řekli, C# je silně objektově orientovaný programovací jazyk. Nemělo by tedy být překvapením, že výjimky jsou také objekty a je tedy možné s nimi podle toho pracovat - tedy volat jejich metody a pracovat s jejich vlastnostmi. Je také možné vytvářet vlastní výjimky, kromě používání již vestavěných. C# má však bohatou zásobu výjimek a pro naše účely by nemělo být nutné přidávat žádné vlastní. Každá výjimka je v jazyce C# potomkem třídy `Exception`. Přesný význam toho, co je to potomek třídy se dozvíte později v kapitole `dědičnost`. Prozatím si jenom řekneme, že výjimek existuje velké množství a všechny vychází z již zmíněné třídy `Exception`.

### Jak vyvolat výjimku?

Každému už se to určitě alespoň jednou podařilo. Z edukativních důvodů si ale ukážeme například `DivideByZeroException`. Jak název napovídá, tuto chybu můžeme vyvolat při pokusu o dělení číslem nula. Vše si ukážeme na jednoduchém příkladě:

<div data-type="code" id="bkmrk-d%C4%9Blen%C3%AD-nulou"><div class="wrapper" data-s-6=""><div class="el-tabs el-tabs--top"><div class="el-tabs__header is-top"><div class="el-tabs__nav-wrap is-top"><div class="el-tabs__nav-scroll"><div class="el-tabs__nav is-top" role="tablist"><div aria-controls="pane-0" aria-selected="true" class="el-tabs__item is-top is-active" id="bkmrk-d%C4%9Blen%C3%AD-nulou-1" role="tab" tabindex="0">Dělení nulou</div></div></div></div></div><div class="el-tabs__content"><div aria-labelledby="tab-0" class="el-tab-pane" role="tabpanel">  
</div></div></div></div></div>```c#
// Naše pomocná proměnná
int i = 0;

// Pokus o dělení nulou
int x = 14 / i;
```

<div data-type="code" id="bkmrk-"><div class="wrapper" data-s-6=""><div class="el-tabs el-tabs--top"><div class="el-tabs__content"><div aria-labelledby="tab-0" class="el-tab-pane" id="bkmrk--1" role="tabpanel"></div></div></div></div></div>Každý jistě z hodin matematiky zná, že v oboru reálných čísel nemá takováto operace žádný smysl. Nulou zkrátka nelze dělit. Stejného názoru je i kompilátor jazyka C# a pokud takováto situace nastane, dojde k neočekávanému ukončení programu a následnému vyhození výjimky `DivideByZeroException`. Jazyk C# nám ale dává možnosti, jak se s neočekávanými situacemi tohoto typu vypořádat. To může být užitečné v momentě, kdy například programujeme kalkulačku a nechceme, aby náš program při dělení nulou padal.

### Blok try-catch

Blok `try-catch` nám umožňuje zachytit výjimku hned po jejím vzniku a na danou situaci patřičně reagovat.

<div class="el-tabs__header is-top" id="bkmrk-v%C3%BDjimky"><div class="el-tabs__nav-wrap is-top"><div class="el-tabs__nav-scroll"><div class="el-tabs__nav is-top" role="tablist"><div aria-controls="pane-0" aria-selected="true" class="el-tabs__item is-top is-active" id="bkmrk-v%C3%BDjimky-1" role="tab" tabindex="0">Výjimky</div></div></div></div></div>```c#
try
{
   int i = 14 / int.Parse(Console.ReadLine());
}
catch
{
   Console.WriteLine("Nelze dělit nulou.");
}
```

<div class="el-tabs__content" id="bkmrk--2"><div aria-labelledby="tab-0" class="el-tab-pane" id="bkmrk--3" role="tabpanel"></div></div>V případě zadání čísla `0` by tedy program dále pokračoval vypsáním řetězce "Nelze dělit nulou." Za výraz catch můžeme ještě dopsat kulaté závorky s libovolným výjimkovým typem čímž docílíme, že odchytávat budeme pouze právě zmíněný typ:

<div class="el-tabs__header is-top" id="bkmrk-zachycen%C3%AD-konkr%C3%A9tn%C3%AD-"><div class="el-tabs__nav-wrap is-top"><div class="el-tabs__nav-scroll"><div class="el-tabs__nav is-top" role="tablist"><div aria-controls="pane-0" aria-selected="true" class="el-tabs__item is-top is-active" id="bkmrk-zachycen%C3%AD-konkr%C3%A9tn%C3%AD--1" role="tab" tabindex="0">Zachycení konkrétní výjimky</div></div></div></div></div>```c#
Form form = null;

try
{
   int x = 100 / form.Size.Width;
}
catch (DivideByZeroExpception ex)
{
   Console.WriteLine(ex.Message);
}
```

<div class="el-tabs__content" id="bkmrk--4"><div aria-labelledby="tab-0" class="el-tab-pane" id="bkmrk--5" role="tabpanel"></div></div>Takovýto fragment kódu by vyvolal `NullReferenceException`, neboť objekt (proměnná) `form` není inicializována - odkazuje na hodnotu `null`. Ačkoliv je celé tvrzení obaleno `try` blokem, dojde i tak k pádu programu a vyhození výjimky, neboť blokem `catch` odchytáváme pouze výjimky typu `DivideByZeroExpception`. V těle bloku `catch` pak vypisujeme obsah chybového sdělení za pomocí vlastnosti `Message`.

### Vlastní vyvolání výjimky

Jazyk C# umožňuje vyvolat v krajních situacích výjimku a to buď naši vlastní nebo již předvytvořenou. Vše ilustruje následující ukázka:

<div class="el-tabs__header is-top" id="bkmrk-vyvol%C3%A1n%C3%AD-v%C3%BDjimky"><div class="el-tabs__nav-wrap is-top"><div class="el-tabs__nav-scroll"><div class="el-tabs__nav is-top" role="tablist"><div aria-controls="pane-0" aria-selected="true" class="el-tabs__item is-top is-active" id="bkmrk-vyvol%C3%A1n%C3%AD-v%C3%BDjimky-1" role="tab" tabindex="0">Vyvolání výjimky</div></div></div></div></div>```c#
throw new Exception("Něco se nepovedlo...");
```

<div class="el-tabs__content" id="bkmrk--6"><div aria-labelledby="tab-0" class="el-tab-pane" id="bkmrk--7" role="tabpanel"></div></div>Klíčové slovo `throw` značí, že vyvoláváme výjimku. Následuje klíčové slovo `new`, jelikož vytváříme novou instanci třídy (objekt) a na závěr následuje název třídy, který označuje, jakou výjimku vyvoláváme. V tomto případě se jedná o obecnou generickou výjimku. Do konstruktoru výjimky můžeme předat řetězec popisující chybu.

### Validace vstupů pomocí tryParse

Pojďme si nyní představit metodu `TryParse()`. Tato metoda slouží pro konverzi mezi datovými typy. Přebírá dva argumenty. Prvním je hodnota, kterou chceme přetypovat a druhým je proměnná, do které se má uložit výsledek konverze. Návratovým typem této metody je `bool`, který signalizuje, zda konverze proběhla v pořádku, či nikoli:

<div class="el-tabs__header is-top" id="bkmrk-bezpe%C4%8Dn%C3%A9-p%C5%99etypov%C3%A1n%C3%AD"><div class="el-tabs__nav-wrap is-top"><div class="el-tabs__nav-scroll"><div class="el-tabs__nav is-top" role="tablist"><div aria-controls="pane-0" aria-selected="true" class="el-tabs__item is-top is-active" id="bkmrk-bezpe%C4%8Dn%C3%A9-p%C5%99etypov%C3%A1n%C3%AD-1" role="tab" tabindex="0">Bezpečné přetypování</div></div></div></div></div>```c#
// Hodnota, jenž budeme převádět na int
string hodnota = "23abc";
int vysledek;

// Přetypování
bool uspech = int.TryParse(hodnota, out vysledek);
```

<div class="el-tabs__content" id="bkmrk--8"><div aria-labelledby="tab-0" class="el-tab-pane" id="bkmrk--9" role="tabpanel"></div></div>Pojďme si nyní rozebrat tento fragment kódu. Na začátku inicializujeme proměnnou, která je typu `bool` a do které bude uloženo, zda konverze proběhla v pořádku. Samotnou metodu `TryParse()` můžeme volat z jakéhokoliv primitivního datového typu. Protože náš řetězec `23abc` se chceme pokusit převést na datový typ `int`, voláme metodu právě z tohoto datového typu. Prvním argumentem je řetězec, který se budeme pokoušet převádět a druhým argumentem je proměnná, do které se v případě úspěšného převodu uloží přetypovaná hodnota. Povšimněte si, že druhému argumentu předchází klíčové slovo `out`. Metoda `TryParse()` nemůže přetypovanou hodnotu vracet, neboť již vrací `bool` symbolizující úspěšnost či neúspěšnost konverze. Potřebujeme-li tedy, aby metoda vracela více jak jednu hodnotu, klíčové slovo `out` je cesta, jak toho docílit.

V případě naší ukázky bude konverze neúspěšná, neboť náš řetězec obsahuje písmena. Proměnná `uspech` by tedy nabyla hodnoty typu `false` a proměnná `vysledek` by zůstala nezměněná.

### Shrnutí

V této kapitole jsme si objasnili práci s výjimkami a třídou `Exception`. Jejich hierarchii, syntaxi, jak je vyvolávat a ošetřovat. Nakonec jsme si řekli, jak pracovat se uživatelským vstupem a ošetřit nesprávné vstupy od uživatele pomocí metody `TryParse()`.