Příkazy zpracování výjimek - throw, try-catchtry-finally, atry-catch-finally

Příkazy a try příkazy slouží throw k práci s výjimkami. Pomocí příkazu throw vyvoláte výjimku. Pomocí příkazu try můžete zachytit a zpracovat výjimky, ke kterým může dojít během provádění bloku kódu.

Příkaz throw

Příkaz throw vyvolá výjimku:

if (shapeAmount <= 0)
{
    throw new ArgumentOutOfRangeException(nameof(shapeAmount), "Amount of shapes must be positive.");
}

throw e; V příkazu musí být výsledek výrazu e implicitně konvertibilní na System.Exception.

Můžete použít předdefinované třídy výjimek, například ArgumentOutOfRangeException nebo InvalidOperationException. .NET také poskytuje pomocné metody pro vyvolání výjimek za určitých podmínek: ArgumentNullException.ThrowIfNull a ArgumentException.ThrowIfNullOrEmpty. Můžete také definovat vlastní třídy výjimek, které jsou odvozeny z System.Exception. Další informace naleznete v tématu Vytváření a vyvolávání výjimek.

catch Uvnitř bloku můžete použít throw; příkaz k opětovnému vyvolání výjimky, která je zpracována blokemcatch:

try
{
    ProcessShapes(shapeAmount);
}
catch (Exception e)
{
    LogError(e, "Shape processing failed.");
    throw;
}

Poznámka:

throw; zachová původní trasování zásobníku výjimky, která je uložena Exception.StackTrace ve vlastnosti. Naopak, throw e; aktualizuje StackTrace vlastnost e.

Při vyvolání výjimky modul CLR (Common Language Runtime) vyhledá catch blok , který může tuto výjimku zpracovat. Pokud aktuálně spuštěná metoda neobsahuje takový catch blok, CLR se podívá na metodu, která volala aktuální metodu atd. Pokud se nenajde žádný catch blok, CLR ukončí spuštěné vlákno. Další informace najdete v části Jak se zpracovávají výjimky specifikace jazyka C#.

Výraz throw

Můžete také použít throw jako výraz. To může být praktické v řadě případů, mezi které patří:

  • podmíněný operátor. Následující příklad používá throw výraz k vyvolání ArgumentException , když je předané pole args prázdné:

    string first = args.Length >= 1 
        ? args[0]
        : throw new ArgumentException("Please supply at least one argument.");
    
  • operátor null-coalescing. Následující příklad používá throw výraz k vyvolání ArgumentNullException , když řetězec přiřadit k vlastnosti je null:

    public string Name
    {
        get => name;
        set => name = value ??
            throw new ArgumentNullException(paramName: nameof(value), message: "Name cannot be null");
    }
    
  • výraz-bodied lambda nebo metoda. Následující příklad používá throw výraz k vyvolání InvalidCastException označující, že převod na DateTime hodnotu není podporován:

    DateTime ToDateTime(IFormatProvider provider) =>
             throw new InvalidCastException("Conversion to a DateTime is not supported.");
    

Příkaz try

Příkaz můžete použít try v některém z následujících formulářů: try-catch – ke zpracování výjimek, ke kterým může dojít při provádění kódu uvnitř try bloku, – k určení kódu, try-finally který se spustí při opuštění try bloku, a try-catch-finally - jako kombinaci předchozích dvou formulářů.

Příkaz try-catch

try-catch Příkaz použijte ke zpracování výjimek, ke kterým může dojít během provádění bloku kódu. Umístěte kód tam, kde může do bloku dojít k výjimce try . Pomocí klauzule catch zadejte základní typ výjimek, které chcete zpracovat v odpovídajícím catch bloku:

try
{
    var result = Process(-3, 4);
    Console.WriteLine($"Processing succeeded: {result}");
}
catch (ArgumentException e)
{
    Console.WriteLine($"Processing failed: {e.Message}");
}

Můžete zadat několik klauzulí catch:

try
{
    var result = await ProcessAsync(-3, 4, cancellationToken);
    Console.WriteLine($"Processing succeeded: {result}");
}
catch (ArgumentException e)
{
    Console.WriteLine($"Processing failed: {e.Message}");
}
catch (OperationCanceledException)
{
    Console.WriteLine("Processing is cancelled.");
}

Pokud dojde k výjimce, jsou klauzule catch zkoumány v zadaném pořadí shora dolů. Při každé vyvolání výjimky se spustí maximálně jeden catch blok. Jak ukazuje předchozí příklad, můžete vynechat deklaraci proměnné výjimky a zadat pouze typ výjimky v klauzuli catch. Klauzule catch bez jakéhokoli zadaného typu výjimky odpovídá jakékoli výjimce a pokud existuje, musí být poslední klauzulí catch.

Pokud chcete znovu vyvolat zachycenou výjimku, použijte příkazthrow, jak ukazuje následující příklad:

try
{
    var result = Process(-3, 4);
    Console.WriteLine($"Processing succeeded: {result}");
}
catch (Exception e)
{
    LogError(e, "Processing failed.");
    throw;
}

Poznámka:

throw; zachová původní trasování zásobníku výjimky, která je uložena Exception.StackTrace ve vlastnosti. Naopak, throw e; aktualizuje StackTrace vlastnost e.

when Filtr výjimek

Spolu s typem výjimky můžete také zadat filtr výjimky, který dále prozkoumá výjimku a rozhodne, jestli odpovídající catch blok zpracovává tuto výjimku. Filtr výjimek je logický výraz, který následuje za when klíčovým slovem, jak ukazuje následující příklad:

try
{
    var result = Process(-3, 4);
    Console.WriteLine($"Processing succeeded: {result}");
}
catch (Exception e) when (e is ArgumentException || e is DivideByZeroException)
{
    Console.WriteLine($"Processing failed: {e.Message}");
}

Předchozí příklad používá filtr výjimek k poskytnutí jednoho catch bloku pro zpracování výjimek dvou zadaných typů.

Pokud se rozlišují filtry výjimek, můžete zadat několik catch klauzulí pro stejný typ výjimky. Jedna z těchto klauzulí nemusí mít žádný filtr výjimek. Pokud taková klauzule existuje, musí to být poslední z klauzulí, které určují tento typ výjimky.

catch Pokud má klauzule filtr výjimek, může určit typ výjimky, který je stejný jako nebo menší odvozený než typ catch výjimky klauzule, která se zobrazí za ní. Pokud je například k dispozici filtr výjimek, catch (Exception e) klauzule nemusí být poslední klauzulí.

Výjimky v asynchronních metodách a metodách iterátoru

Pokud v asynchronní funkci dojde k výjimce, rozšíří se do volajícího funkce, když očekáváte výsledek funkce, jak ukazuje následující příklad:

public static async Task Run()
{
    try
    {
        Task<int> processing = ProcessAsync(-1);
        Console.WriteLine("Launched processing.");

        int result = await processing;
        Console.WriteLine($"Result: {result}.");
    }
    catch (ArgumentException e)
    {
        Console.WriteLine($"Processing failed: {e.Message}");
    }
    // Output:
    // Launched processing.
    // Processing failed: Input must be non-negative. (Parameter 'input')
}

private static async Task<int> ProcessAsync(int input)
{
    if (input < 0)
    {
        throw new ArgumentOutOfRangeException(nameof(input), "Input must be non-negative.");
    }

    await Task.Delay(500);
    return input;
}

Pokud v metodě iterátoru dojde k výjimce, rozšíří se do volajícího pouze v případě, že iterátor přejde na další prvek.

Příkaz try-finally

try-finally V příkazu finally se blok spustí, když ovládací prvek opustí try blok. Ovládací prvek může blok opustit try v důsledku

  • normální provádění,
  • provedení příkazu skoku (to znamená , return, break, continuenebo ) nebo goto
  • šíření výjimky z try bloku.

Následující příklad používá finally blok k resetování stavu objektu před tím, než ovládací prvek opustí metodu:

public async Task HandleRequest(int itemId, CancellationToken ct)
{
    Busy = true;

    try
    {
        await ProcessAsync(itemId, ct);
    }
    finally
    {
        Busy = false;
    }
}

Blok můžete také použít finally k vyčištění přidělených prostředků použitých v try bloku.

Poznámka:

Pokud typ prostředku implementuje IDisposable nebo IAsyncDisposable rozhraní, zvažte using příkaz. Příkaz using zajistí, že získané prostředky budou uvolněny, když ovládací prvek opustí using příkaz. Kompilátor transformuje using příkaz na try-finally příkaz.

V téměř všech případech finally se bloky spouští. Jediné případy, kdy finally se bloky nespustí, zahrnují okamžité ukončení programu. K takovému ukončení může dojít například kvůli Environment.FailFast volání nebo výjimce OverflowExceptionInvalidProgramException . Většina operačních systémů provádí rozumné vyčištění prostředků v rámci zastavení a uvolnění procesu.

Příkaz try-catch-finally

K zpracování výjimek, ke kterým může dojít během provádění try bloku, použijete try-catch-finally příkaz oba a zadáte kód, který se musí spustit, když ovládací prvek opustí try příkaz:

public async Task ProcessRequest(int itemId, CancellationToken ct)
{
    Busy = true;

    try
    {
        await ProcessAsync(itemId, ct);
    }
    catch (Exception e) when (e is not OperationCanceledException)
    {
        LogError(e, $"Failed to process request for item ID {itemId}.");
        throw;
    }
    finally
    {
        Busy = false;
    }

}

Při zpracování výjimky blokem catchfinally se blok spustí po spuštění catch tohoto bloku (i když během provádění catch bloku dojde k jiné výjimce). Informace o catch příkazech a finally blocích naleznete v části Příkaztry-catch a Příkaztry-finally.

specifikace jazyka C#

Další informace najdete v následujících částech specifikace jazyka C#:

Viz také