Undantagshanteringsuttryck – throw
, try-catch
, try-finally
och try-catch-finally
Du använder throw
instruktionerna och try
för att arbeta med undantag. Använd -instruktionen throw
för att utlösa ett undantag. Använd -instruktionen try
för att fånga och hantera undantag som kan inträffa under körningen av ett kodblock.
Instruktionen throw
Instruktionen throw
utlöser ett undantag:
if (shapeAmount <= 0)
{
throw new ArgumentOutOfRangeException(nameof(shapeAmount), "Amount of shapes must be positive.");
}
I en throw e;
instruktion måste uttryckets e
resultat implicit konverteras till System.Exception.
Du kan använda de inbyggda undantagsklasserna, till exempel ArgumentOutOfRangeException eller InvalidOperationException. .NET tillhandahåller också följande hjälpmetoder för att utlösa undantag under vissa villkor: ArgumentNullException.ThrowIfNull och ArgumentException.ThrowIfNullOrEmpty. Du kan också definiera dina egna undantagsklasser som härleds från System.Exception. Mer information finns i Skapa och utlösa undantag.
I ett catch
block kan du använda en throw;
-instruktion för att återskapa undantaget som hanteras av catch
blocket:
try
{
ProcessShapes(shapeAmount);
}
catch (Exception e)
{
LogError(e, "Shape processing failed.");
throw;
}
Kommentar
throw;
bevarar den ursprungliga stackspårningen av undantaget, som lagras i Exception.StackTrace egenskapen. I motsats till det throw e;
uppdaterar egenskapen e
för StackTrace .
När ett undantag utlöses letar CLR (Common Language Runtime) efter det catch
block som kan hantera det här undantaget. Om den metod som körs för närvarande inte innehåller något catch
sådant block tittar CLR på metoden som anropade den aktuella metoden och så vidare upp i anropsstacken. Om inget catch
block hittas avslutar CLR den körande tråden. Mer information finns i avsnittet Hur undantag hanteras i C#-språkspecifikationen.
Uttrycket throw
Du kan också använda throw
som ett uttryck. Detta kan vara praktiskt i ett antal fall, bland annat:
villkorsoperatorn. I följande exempel används ett
throw
uttryck för att utlösa en ArgumentException när den skickade matrisenargs
är tom:string first = args.Length >= 1 ? args[0] : throw new ArgumentException("Please supply at least one argument.");
operatorn null-coalescing. I följande exempel används ett
throw
uttryck för att utlösa en ArgumentNullException när strängen som ska tilldelas till en egenskap ärnull
:public string Name { get => name; set => name = value ?? throw new ArgumentNullException(paramName: nameof(value), message: "Name cannot be null"); }
en uttrycksfyllig lambda eller metod. I följande exempel används ett
throw
uttryck för att utlösa ett InvalidCastException för att indikera att en konvertering till ett DateTime värde inte stöds:DateTime ToDateTime(IFormatProvider provider) => throw new InvalidCastException("Conversion to a DateTime is not supported.");
Instruktionen try
Du kan använda -instruktionen try
i något av följande formulär: try-catch
- för att hantera undantag som kan inträffa under körningen av koden i ett try
block, try-finally
- för att ange den kod som körs när kontrollen lämnar try
blocket och try-catch-finally
- som en kombination av de föregående två formulären.
Instruktionen try-catch
Använd -instruktionen try-catch
för att hantera undantag som kan inträffa under körningen av ett kodblock. Placera koden där ett undantag kan inträffa i ett try
block. Använd en catch-sats för att ange den bastyp av undantag som du vill hantera i motsvarande catch
block:
try
{
var result = Process(-3, 4);
Console.WriteLine($"Processing succeeded: {result}");
}
catch (ArgumentException e)
{
Console.WriteLine($"Processing failed: {e.Message}");
}
Du kan ange flera catch-satser:
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.");
}
När ett undantag inträffar granskas catch-satser i den angivna ordningen, uppifrån och ned. Maximalt körs endast ett catch
block för undantag som genereras. Som föregående exempel också visar kan du utelämna deklarationen av en undantagsvariabel och endast ange undantagstypen i en catch-sats. En catch-sats utan någon angiven undantagstyp matchar alla undantag och måste, om det finns, vara den sista catch-satsen.
Om du vill återskapa ett undantag som fångas använder du -instruktionenthrow
, som följande exempel visar:
try
{
var result = Process(-3, 4);
Console.WriteLine($"Processing succeeded: {result}");
}
catch (Exception e)
{
LogError(e, "Processing failed.");
throw;
}
Kommentar
throw;
bevarar den ursprungliga stackspårningen av undantaget, som lagras i Exception.StackTrace egenskapen. I motsats till det throw e;
uppdaterar egenskapen e
för StackTrace .
Ett when
undantagsfilter
Tillsammans med en undantagstyp kan du också ange ett undantagsfilter som ytterligare undersöker ett undantag och avgör om motsvarande catch
block hanterar undantaget. Ett undantagsfilter är ett booleskt uttryck som följer nyckelordet when
, vilket visas i följande exempel:
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}");
}
I föregående exempel används ett undantagsfilter för att tillhandahålla ett enda catch
block för att hantera undantag av två angivna typer.
Du kan ange flera catch
satser för samma undantagstyp om de skiljer sig från undantagsfilter. En av dessa satser kanske inte har något undantagsfilter. Om en sådan sats finns måste den vara den sista av de satser som anger den undantagstypen.
Om en catch
sats har ett undantagsfilter kan den ange den undantagstyp som är samma som eller mindre härledd än en undantagstyp för en catch
sats som visas efter den. Om det till exempel finns ett undantagsfilter behöver en catch (Exception e)
sats inte vara den sista satsen.
Undantag i asynkrona metoder och iteratormetoder
Om ett undantag inträffar i en asynkron funktion sprids det till funktionens anropare när du väntar på resultatet av funktionen, som följande exempel visar:
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;
}
Om ett undantag inträffar i en iteratormetod sprids det endast till anroparen när iteratorn avancerar till nästa element.
Instruktionen try-finally
I en try-finally
instruktion finally
körs blocket när kontrollen lämnar try
blocket. Kontrollen kan lämna try
blocket på grund av
- normal körning,
- körning av en jump-instruktion (dvs. ,
break
return
,continue
ellergoto
) eller - spridning av ett undantag från
try
blocket.
I följande exempel används finally
blocket för att återställa tillståndet för ett objekt innan kontrollen lämnar metoden:
public async Task HandleRequest(int itemId, CancellationToken ct)
{
Busy = true;
try
{
await ProcessAsync(itemId, ct);
}
finally
{
Busy = false;
}
}
Du kan också använda finally
blocket för att rensa allokerade resurser som används i try
blocket.
Kommentar
När typen av en resurs implementerar gränssnittet eller bör du överväga -instruktionenusing
.IAsyncDisposable IDisposable -instruktionen using
säkerställer att förvärvade resurser tas bort när kontrollen lämnar -instruktionen using
. Kompilatorn omvandlar en using
instruktion till en try-finally
-instruktion.
Körningen finally
av blocket beror på om operativsystemet väljer att utlösa en undantagsåtgärd. De enda fall där finally
block inte körs omfattar omedelbar avslutning av ett program. En sådan avslutning kan till exempel inträffa på grund av anropet Environment.FailFast eller ett OverflowException undantag InvalidProgramException . De flesta operativsystem utför en rimlig resursrensning som en del av att stoppa och ta bort processen.
Instruktionen try-catch-finally
Du använder en try-catch-finally
instruktion både för att hantera undantag som kan inträffa under körningen try
av blocket och ange den kod som måste köras när kontrollen lämnar instruktionen try
:
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;
}
}
När ett undantag hanteras av ett catch
block finally
körs blocket efter körningen av blocket catch
(även om ett annat undantag inträffar under körningen catch
av blocket). Information om catch
och block finns i instruktionen try-catch
respektive instruktionsavsnittentry-finally
.finally
Språkspecifikation för C#
Mer information finns i följande avsnitt i C#-språkspecifikationen: