Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Příkazy zpracování výjimek -
Příkazy a throw příkazy slouží try 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 následující 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á
throwvýraz k vyvolání ArgumentException , když je předané poleargsprá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á
throwvýraz k vyvolání ArgumentNullException , když řetězec přiřadit k vlastnosti jenull: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á
throwvý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í.
Filtry výjimek versus tradiční zpracování výjimek
Filtry výjimek poskytují významné výhody oproti tradičním přístupům pro zpracování výjimek. Hlavní rozdíl spočívá v vyhodnocení logiky zpracování výjimek:
-
Filtry výjimek (
when): Výraz filtru se vyhodnotí před zrušením zásobníku. To znamená, že původní zásobník volání a všechny místní proměnné zůstanou během vyhodnocování filtru nedotčené. -
Tradiční
catchbloky: Blok catch se spustí po rozbalení zásobníku a potenciálně ztratí cenné informace o ladění.
Tady je porovnání znázorňující rozdíl:
public static void DemonstrateStackUnwindingDifference()
{
var localVariable = "Important debugging info";
try
{
ProcessWithExceptionFilter(localVariable);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("filter"))
{
// Exception filter: Stack not unwound yet.
// localVariable is still accessible in debugger.
// Call stack shows original throwing location.
Console.WriteLine($"Caught with filter: {ex.Message}");
Console.WriteLine($"Local variable accessible: {localVariable}");
}
try
{
ProcessWithTraditionalCatch(localVariable);
}
catch (InvalidOperationException ex)
{
// Traditional catch: Stack already unwound.
// Some debugging information may be lost.
if (ex.Message.Contains("traditional"))
{
Console.WriteLine($"Caught with if: {ex.Message}");
Console.WriteLine($"Local variable accessible: {localVariable}");
}
else
{
throw; // Re-throws and further modifies stack trace.
}
}
}
private static void ProcessWithExceptionFilter(string context)
{
throw new InvalidOperationException($"Exception for filter demo: {context}");
}
private static void ProcessWithTraditionalCatch(string context)
{
throw new InvalidOperationException($"Exception for traditional demo: {context}");
}
Výhody filtrů výjimek
- Lepší možnosti ladění: Vzhledem k tomu, že zásobník není unwound, dokud filtr neodpovídá, ladicí programy mohou zobrazit původní bod selhání se všemi místními proměnnými beze změny.
- Výhody výkonu: Pokud žádný filtr neodpovídá, výjimka se bude dál šířit bez režie při odvíjení a obnovení zásobníku.
- Čistější kód: Více filtrů dokáže zpracovat různé podmínky stejného typu výjimky bez nutnosti vnořených příkazů if-else.
- Protokolování a diagnostika: Před rozhodnutím, jestli se má výjimka zpracovat, můžete prozkoumat podrobnosti o výjimce a protokolovat:
public static void DemonstrateDebuggingAdvantage()
{
var contextData = new Dictionary<string, object>
{
["RequestId"] = Guid.NewGuid(),
["UserId"] = "user123",
["Timestamp"] = DateTime.Now
};
try
{
// Simulate a deep call stack.
Level1Method(contextData);
}
catch (Exception ex) when (LogAndFilter(ex, contextData))
{
// This catch block may never execute if LogAndFilter returns false.
// But LogAndFilter can examine the exception while the stack is intact.
Console.WriteLine("Exception handled after logging");
}
}
private static void Level1Method(Dictionary<string, object> context)
{
Level2Method(context);
}
private static void Level2Method(Dictionary<string, object> context)
{
Level3Method(context);
}
private static void Level3Method(Dictionary<string, object> context)
{
throw new InvalidOperationException("Error in deep call stack");
}
private static bool LogAndFilter(Exception ex, Dictionary<string, object> context)
{
// This method runs before stack unwinding.
// Full call stack and local variables are still available.
Console.WriteLine($"Exception occurred: {ex.Message}");
Console.WriteLine($"Request ID: {context["RequestId"]}");
Console.WriteLine($"Full stack trace preserved: {ex.StackTrace}");
// Return true to handle the exception, false to continue search.
return ex.Message.Contains("deep call stack");
}
Kdy použít filtry výjimek
Filtry výjimek použijte v případě, že potřebujete:
- Zpracování výjimek na základě konkrétních podmínek nebo vlastností
- Zachovejte původní zásobník volání pro ladění.
- Než se rozhodnete, jestli se mají zpracovat, zalogujte nebo prozkoumejte výjimky.
- Zpracovává stejný typ výjimky odlišně na základě kontextu.
public static void HandleFileOperations(string filePath)
{
try
{
// Simulate file operation that might fail.
ProcessFile(filePath);
}
catch (IOException ex) when (ex.Message.Contains("access denied"))
{
Console.WriteLine("File access denied. Check permissions.");
}
catch (IOException ex) when (ex.Message.Contains("not found"))
{
Console.WriteLine("File not found. Verify the path.");
}
catch (IOException ex) when (IsNetworkPath(filePath))
{
Console.WriteLine($"Network file operation failed: {ex.Message}");
}
catch (IOException)
{
Console.WriteLine("Other I/O error occurred.");
}
}
private static void ProcessFile(string filePath)
{
// Simulate different types of file exceptions.
if (filePath.Contains("denied"))
throw new IOException("File access denied");
if (filePath.Contains("missing"))
throw new IOException("File not found");
if (IsNetworkPath(filePath))
throw new IOException("Network timeout occurred");
}
private static bool IsNetworkPath(string path)
{
return path.StartsWith(@"\\") || path.StartsWith("http");
}
Zachování trasování zásobníku
Filtry výjimek zachovávají původní ex.StackTrace vlastnost.
catch Pokud klauzule nemůže zpracovat výjimku a znovu vyvolá, dojde ke ztrátě informací o původním zásobníku.
when Filtr nevyvolá zásobník, takže pokud when je falsefiltr , původní trasování zásobníku se nezmění.
Přístup k filtrování výjimek je cenný v aplikacích, kde zachování informací o ladění je zásadní pro diagnostiku problémů.
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 ) nebogoto - šíření výjimky z
trybloku.
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.
finally Spuštění bloku závisí na tom, jestli se operační systém rozhodne aktivovat operaci uvolnění výjimky. 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-catch-finally bloku, použijete try 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 try-catch a Příkaztry-finally.
specifikace jazyka C#
Další informace najdete v následujících částech specifikace jazyka C#: