try-catch (Referenční dokumentace jazyka C#)

Příkaz try-catch se skládá z bloku následované jednou nebo více klauzulemi, které určují obslužné rutiny try catch pro různé výjimky.

Když je vyvolána výjimka, modul CLR (Common Language Runtime) vyhledá catch příkaz, který tuto výjimku zpracuje. Pokud aktuálně spuštěná metoda takový blok neobsahuje, modul CLR se podívá na metodu, která volala aktuální metodu, a tak dále v catch zásobníku volání. Pokud není nalezen žádný blok, modul CLR zobrazí uživateli zprávu o neošetřené výjimce a zastaví catch provádění programu.

Blok try obsahuje strážené kódy, které mohou způsobit výjimku. Blok se spustí, dokud není vyvolána výjimka nebo se úspěšně nedokončí. Například následující pokus o přetypování null objektu vyvolá NullReferenceException výjimku:

object o2 = null;
try
{
    int i2 = (int)o2;   // Error
}

I když catch klauzuli lze použít bez argumentů k zachycení jakéhokoli typu výjimky, toto použití se nedoporučuje. Obecně platí, že byste měli zachytit pouze výjimky, ze které víte, jak se zotavit. Proto byste měli vždy zadat argument objektu odvozený z System.Exception . Typ výjimky by měl být co nejvíce specifický, aby se zabránilo nesprávnému přijetí výjimek, které obslužná rutina výjimky ve skutečnosti nemůže vyřešit. V takovém případě upřednostňte konkrétní výjimky před základním Exception typem. Příklad:

catch (InvalidCastException e)
{
    // recover from exception
}

Ve stejném příkazu try-catch je možné použít více než jednu konkrétní catch klauzuli. V tomto případě je pořadí klauzulí důležité, protože klauzule jsou catch catch prozkoumány v pořadí. Zachyťte konkrétnější výjimky před méně specifickými výjimkami. Kompilátor vygeneruje chybu, pokud zachytáte bloky tak, aby pozdější blok nebyl nikdy dosažen.

Použití catch argumentů je jedním ze způsob, jak filtrovat výjimky, které chcete zpracovat. Můžete také použít filtr výjimek, který výjimku dále prozkoumá a rozhodne, jestli ji má zpracovat. Pokud filtr výjimek vrátí hodnotu false, bude vyhledávání obslužné rutiny pokračovat.

catch (ArgumentException e) when (e.ParamName == "…")
{
    // recover from exception
}

Filtry výjimek jsou vhodnější k zachytání a opětovnému vytyčování (vysvětleno níže), protože filtry necháte zásobník bez sdílení. Pokud později obslužná rutina vy výpisu zásobníku, můžete zjistit, odkud výjimka původně pochází, a nikoli pouze poslední místo, kde byla znovu vyvolána. Běžným použitím výrazů filtru výjimek je protokolování. Můžete vytvořit filtr, který vždy vrátí hodnotu false, která také vystupuje do protokolu. Výjimky můžete protokolovat tak, jak se odchýlují, aniž byste je museli zpracovávat a znovu vytvářet.

Příkaz throw lze použít v bloku k opětovného vyvolání catch výjimky zachycené catch příkazem . Následující příklad extrahuje informace o zdroji z výjimky a potom vyvolá IOException výjimku nadřazené metodě.

catch (FileNotFoundException e)
{
    // FileNotFoundExceptions are handled here.
}
catch (IOException e)
{
    // Extract some information from this exception, and then
    // throw it to the parent method.
    if (e.Source != null)
        Console.WriteLine("IOException source: {0}", e.Source);
    throw;
}

Můžete zachytit jednu výjimku a vyvolat jinou výjimku. Pokud to chcete provést, zadejte výjimku, kterou jste zachytili jako vnitřní výjimku, jak je znázorněno v následujícím příkladu.

catch (InvalidCastException e)
{
    // Perform some action here, and then throw a new exception.
    throw new YourCustomException("Put your error message here.", e);
}

Můžete také znovu vyvolat výjimku, pokud je zadaná podmínka pravdivá, jak je znázorněno v následujícím příkladu.

catch (InvalidCastException e)
{
    if (e.Data == null)
    {
        throw;
    }
    else
    {
        // Take some action.
    }
}

Poznámka

Je také možné použít filtr výjimek k získání podobného výsledku často čistějším způsobem (a také neupravovat zásobník, jak je vysvětleno výše v tomto dokumentu). Následující příklad má podobné chování pro volající jako předchozí příklad. Funkce vyvolá zpět InvalidCastException volajícímu, když e.Data je null .

catch (InvalidCastException e) when (e.Data != null)
{
    // Take some action.
}

Z bloku try inicializujte pouze proměnné, které jsou v bloku deklarovány. V opačném případě může dojít k výjimce před dokončením provádění bloku. Například v následujícím příkladu kódu je proměnná n inicializována uvnitř try bloku . Pokus o použití této proměnné mimo try blok v Write(n) příkazu vygeneruje chybu kompilátoru.

static void Main()
{
    int n;
    try
    {
        // Do not initialize this variable here.
        n = 123;
    }
    catch
    {
    }
    // Error: Use of unassigned local variable 'n'.
    Console.Write(n);
}

Další informace o úchytu najdete v tématu try-catch-finally.

Výjimky v asynchronních metodách

Asynchronní metoda je označena asynchronním modifikátorem a obvykle obsahuje jeden nebo více výrazů nebo příkazů await. Výraz await použije operátor await na Task nebo Task<TResult> .

Když ovládací prvek dosáhne v asynchronní metodě, průběh v metodě je await pozastaven, dokud se nedokončila čekající úloha. Po dokončení úlohy může provádění pokračovat v metodě . Další informace najdete v tématu Asynchronní programování s modifikátorem async a operátorem await.

Dokončená úloha, na kterou je použit , může být v chybovém stavu kvůli neošetřené výjimce v await metodě, která vrací úlohu. Čekání na úlohu vyvolá výjimku. Úloha může také skončit ve zrušené stavu, pokud je asynchronní proces, který ji vrací, zrušen. Čekání na zrušenou úlohu vyvolá OperationCanceledException .

Chcete-li zachytit výjimku, čekejte na úlohu v bloku a zachyťte try výjimku v přidruženém catch bloku. Příklad najdete v části příkladu metody Async.

Úloha může být ve chybovém stavu, protože v očekávané asynchronní metodě došlo k více výjimce. Úloha může být například výsledkem volání Task.WhenAll metody . Když čekáte na takovou úlohu, zachytí se pouze jedna z výjimek a nemůžete předpovědět, která výjimka bude zachycena. Příklad najdete v části příkladu Task.WhenAll.

Příklad

V následujícím příkladu try obsahuje blok volání metody , které může způsobit ProcessString výjimku. Klauzule catch obsahuje obslužnou rutinu výjimky, která pouze zobrazí zprávu na obrazovce. Když je příkaz volán z uvnitř , systém vyhledá příkaz a throw ProcessString zobrazí zprávu catch Exception caught .

class TryFinallyTest
{
    static void ProcessString(string s)
    {
        if (s == null)
        {
            throw new ArgumentNullException(paramName: nameof(s), message: "parameter can't be null.");
        }
    }

    public static void Main()
    {
        string s = null; // For demonstration purposes.

        try
        {
            ProcessString(s);
        }
        catch (Exception e)
        {
            Console.WriteLine("{0} Exception caught.", e);
        }
    }
}
/*
Output:
System.ArgumentNullException: Value cannot be null.
   at TryFinallyTest.Main() Exception caught.
 * */

Příklad dvou bloků catch

V následujícím příkladu se používají dva bloky catch a zachytí se nej specifická výjimka, která přichází jako první.

Pokud chcete zachytit nejméně specifickou výjimku, můžete nahradit příkaz throw v ProcessString následujícím příkazem: throw new Exception() .

Pokud v příkladu nejprve umístěte blok catch s nejmenším konkrétním umístěním, zobrazí se následující chybová zpráva: A previous catch clause already catches all exceptions of this or a super type ('System.Exception') .

class ThrowTest3
{
    static void ProcessString(string s)
    {
        if (s == null)
        {
            throw new ArgumentNullException(paramName: nameof(s), message: "Parameter can't be null");
        }
    }

    public static void Main()
    {
        try
        {
            string s = null;
            ProcessString(s);
        }
        // Most specific:
        catch (ArgumentNullException e)
        {
            Console.WriteLine("{0} First exception caught.", e);
        }
        // Least specific:
        catch (Exception e)
        {
            Console.WriteLine("{0} Second exception caught.", e);
        }
    }
}
/*
 Output:
 System.ArgumentNullException: Value cannot be null.
 at Test.ThrowTest3.ProcessString(String s) ... First exception caught.
*/

Příklad asynchronní metody

Následující příklad znázorňuje zpracování výjimek pro asynchronní metody. Pokud chcete zachytit výjimku, kterou asynchronní úloha vyvolá, umístěte výraz do bloku a zachyťte await try výjimku v catch bloku.

Pokud chcete předvést throw new Exception zpracování výjimek, odkomentování řádku v příkladu. Vlastnost úkolu je nastavena na , vlastnost úkolu je nastavena na výjimku a IsFaulted True výjimka je Exception.InnerException zachycena v catch bloku.

Odkomentování throw new OperationCanceledException řádku, aby bylo předvedení, co se stane, když zrušíte asynchronní proces. Vlastnost úkolu IsCanceled je nastavená na true a výjimka je zachycena v bloku catch . Za některých podmínek, které se na tento příklad nevztahují, je vlastnost úkolu nastavená na a IsFaulted true je IsCanceled nastavená na false .

public async Task DoSomethingAsync()
{
    Task<string> theTask = DelayAsync();

    try
    {
        string result = await theTask;
        Debug.WriteLine("Result: " + result);
    }
    catch (Exception ex)
    {
        Debug.WriteLine("Exception Message: " + ex.Message);
    }
    Debug.WriteLine("Task IsCanceled: " + theTask.IsCanceled);
    Debug.WriteLine("Task IsFaulted:  " + theTask.IsFaulted);
    if (theTask.Exception != null)
    {
        Debug.WriteLine("Task Exception Message: "
            + theTask.Exception.Message);
        Debug.WriteLine("Task Inner Exception Message: "
            + theTask.Exception.InnerException.Message);
    }
}

private async Task<string> DelayAsync()
{
    await Task.Delay(100);

    // Uncomment each of the following lines to
    // demonstrate exception handling.

    //throw new OperationCanceledException("canceled");
    //throw new Exception("Something happened.");
    return "Done";
}

// Output when no exception is thrown in the awaited method:
//   Result: Done
//   Task IsCanceled: False
//   Task IsFaulted:  False

// Output when an Exception is thrown in the awaited method:
//   Exception Message: Something happened.
//   Task IsCanceled: False
//   Task IsFaulted:  True
//   Task Exception Message: One or more errors occurred.
//   Task Inner Exception Message: Something happened.

// Output when a OperationCanceledException or TaskCanceledException
// is thrown in the awaited method:
//   Exception Message: canceled
//   Task IsCanceled: True
//   Task IsFaulted:  False

Příklad Task.WhenAll

Následující příklad znázorňuje zpracování výjimek, kdy může více úloh vést k více výjimce. Blok try čeká na úlohu vrácenou voláním Task.WhenAll . Úloha je dokončena po dokončení tří úkolů, na které se použije WhenAll.

Každý z těchto tří úkolů způsobí výjimku. Blok iteruje výjimkami, které jsou nalezeny ve vlastnosti úlohy, catch Exception.InnerExceptions která byla vrácena pomocí Task.WhenAll .

public async Task DoMultipleAsync()
{
    Task theTask1 = ExcAsync(info: "First Task");
    Task theTask2 = ExcAsync(info: "Second Task");
    Task theTask3 = ExcAsync(info: "Third Task");

    Task allTasks = Task.WhenAll(theTask1, theTask2, theTask3);

    try
    {
        await allTasks;
    }
    catch (Exception ex)
    {
        Debug.WriteLine("Exception: " + ex.Message);
        Debug.WriteLine("Task IsFaulted: " + allTasks.IsFaulted);
        foreach (var inEx in allTasks.Exception.InnerExceptions)
        {
            Debug.WriteLine("Task Inner Exception: " + inEx.Message);
        }
    }
}

private async Task ExcAsync(string info)
{
    await Task.Delay(100);

    throw new Exception("Error-" + info);
}

// Output:
//   Exception: Error-First Task
//   Task IsFaulted: True
//   Task Inner Exception: Error-First Task
//   Task Inner Exception: Error-Second Task
//   Task Inner Exception: Error-Third Task

specifikace jazyka C#

Další informace najdete v části s příkazem try specifikace jazyka C#.

Viz také