Instructies voor het afhandelen van uitzonderingen - throw
, try-catch
, try-finally
en try-catch-finally
U gebruikt de throw
en try
instructies om te werken met uitzonderingen. Gebruik de throw
instructie om een uitzondering te genereren. Gebruik de try
instructie om uitzonderingen te ondervangen en af te handelen die kunnen optreden tijdens het uitvoeren van een codeblok.
De throw
instructie
De throw
instructie genereert een uitzondering:
if (shapeAmount <= 0)
{
throw new ArgumentOutOfRangeException(nameof(shapeAmount), "Amount of shapes must be positive.");
}
In een throw e;
instructie moet het resultaat van de expressie e
impliciet worden omgezet in System.Exception.
U kunt bijvoorbeeld de ingebouwde uitzonderingsklassen gebruiken, ArgumentOutOfRangeException of InvalidOperationException. .NET biedt ook de volgende helpermethoden voor het genereren van uitzonderingen in bepaalde voorwaarden: ArgumentNullException.ThrowIfNull en ArgumentException.ThrowIfNullOrEmpty. U kunt ook uw eigen uitzonderingsklassen definiëren die zijn afgeleid van System.Exception. Zie Uitzonderingen maken en genereren voor meer informatie.
In een catch
blok kunt u een throw;
instructie gebruiken om de uitzondering die wordt verwerkt door het catch
blok opnieuw te genereren:
try
{
ProcessShapes(shapeAmount);
}
catch (Exception e)
{
LogError(e, "Shape processing failed.");
throw;
}
Notitie
throw;
behoudt de oorspronkelijke stacktracering van de uitzondering, die is opgeslagen in de Exception.StackTrace eigenschap. In tegenstelling tot dat, throw e;
werkt de StackTrace eigenschap van e
.
Wanneer er een uitzondering wordt gegenereerd, zoekt de Common Language Runtime (CLR) naar het catch
blok dat deze uitzondering kan verwerken. Als de momenteel uitgevoerde methode geen dergelijk catch
blok bevat, kijkt de CLR naar de methode die de huidige methode wordt genoemd, enzovoort naar de aanroepstack. Als er geen catch
blok wordt gevonden, beëindigt de CLR de uitvoeringsthread. Zie de sectie Hoe uitzonderingen worden verwerkt in de C#-taalspecificatie voor meer informatie.
De throw
expressie
U kunt ook als expressie gebruiken throw
. Dit kan handig zijn in een aantal gevallen, waaronder:
de voorwaardelijke operator. In het volgende voorbeeld wordt een expressie gebruikt om een
throw
ArgumentException expressie te werpen wanneer de doorgegeven matrixargs
leeg is:string first = args.Length >= 1 ? args[0] : throw new ArgumentException("Please supply at least one argument.");
de operator null-coalescing. In het volgende voorbeeld wordt een expressie gebruikt om een
throw
ArgumentNullException expressie te genereren wanneer de tekenreeks die aan een eigenschap moet worden toegewezen, isnull
:public string Name { get => name; set => name = value ?? throw new ArgumentNullException(paramName: nameof(value), message: "Name cannot be null"); }
een expressie-bodied lambda of methode. In het volgende voorbeeld wordt een expressie gebruikt om aan
throw
te InvalidCastException geven dat een conversie naar een DateTime waarde niet wordt ondersteund:DateTime ToDateTime(IFormatProvider provider) => throw new InvalidCastException("Conversion to a DateTime is not supported.");
De try
instructie
U kunt de try
instructie in een van de volgende formulieren gebruiken: try-catch
- om uitzonderingen af te handelen die kunnen optreden tijdens het uitvoeren van de code binnen een try
blok, try-finally
om de code op te geven die wordt uitgevoerd wanneer het besturingselement het try
blok verlaat en try-catch-finally
- als een combinatie van de voorgaande twee formulieren.
De try-catch
instructie
Gebruik de try-catch
instructie om uitzonderingen af te handelen die kunnen optreden tijdens het uitvoeren van een codeblok. Plaats de code waarin een uitzondering zich in een try
blok kan voordoen. Gebruik een catch-component om het basistype uitzonderingen op te geven dat u wilt verwerken in het bijbehorende catch
blok:
try
{
var result = Process(-3, 4);
Console.WriteLine($"Processing succeeded: {result}");
}
catch (ArgumentException e)
{
Console.WriteLine($"Processing failed: {e.Message}");
}
U kunt verschillende catch-componenten opgeven:
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.");
}
Wanneer er een uitzondering optreedt, worden catch-componenten in de opgegeven volgorde onderzocht, van boven naar beneden. Maximaal wordt slechts één catch
blok uitgevoerd voor een gegenereerde uitzondering. Zoals in het voorgaande voorbeeld ook wordt weergegeven, kunt u de declaratie van een uitzonderingsvariabele weglaten en alleen het uitzonderingstype opgeven in een catch-component. Een catch-component zonder een opgegeven uitzonderingstype komt overeen met een uitzondering en, indien aanwezig, moet de laatste catch-component zijn.
Als u een betrapte uitzondering opnieuw wilt genereren, gebruikt u de throw
instructie, zoals in het volgende voorbeeld wordt weergegeven:
try
{
var result = Process(-3, 4);
Console.WriteLine($"Processing succeeded: {result}");
}
catch (Exception e)
{
LogError(e, "Processing failed.");
throw;
}
Notitie
throw;
behoudt de oorspronkelijke stacktracering van de uitzondering, die is opgeslagen in de Exception.StackTrace eigenschap. In tegenstelling tot dat, throw e;
werkt de StackTrace eigenschap van e
.
Een when
uitzonderingsfilter
Samen met een uitzonderingstype kunt u ook een uitzonderingsfilter opgeven waarmee een uitzondering verder wordt onderzocht en wordt bepaald of die uitzondering door het bijbehorende catch
blok wordt verwerkt. Een uitzonderingsfilter is een Boole-expressie die het trefwoord volgt when
, zoals in het volgende voorbeeld wordt weergegeven:
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}");
}
In het voorgaande voorbeeld wordt een uitzonderingsfilter gebruikt om één catch
blok te bieden voor het afhandelen van uitzonderingen van twee opgegeven typen.
U kunt verschillende catch
componenten opgeven voor hetzelfde uitzonderingstype als ze onderscheid maken tussen uitzonderingsfilters. Een van deze componenten heeft mogelijk geen uitzonderingsfilter. Als er een dergelijke component bestaat, moet dit de laatste van de componenten zijn die dat uitzonderingstype opgeven.
Als een catch
component een uitzonderingsfilter heeft, kan het uitzonderingstype opgeven dat hetzelfde is als of minder is afgeleid dan een uitzonderingstype van een catch
component die erna wordt weergegeven. Als er bijvoorbeeld een uitzonderingsfilter aanwezig is, hoeft een catch (Exception e)
component niet de laatste component te zijn.
Uitzonderingen in asynchrone en iteratormethoden
Als er een uitzondering optreedt in een asynchrone functie, wordt deze doorgegeven aan de aanroeper van de functie wanneer u wacht op het resultaat van de functie, zoals in het volgende voorbeeld wordt weergegeven:
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;
}
Als er een uitzondering optreedt in een iteratormethode, wordt deze alleen doorgegeven aan de aanroeper wanneer de iterator naar het volgende element gaat.
De try-finally
instructie
In een try-finally
instructie wordt het blok uitgevoerd wanneer het finally
besturingselement het try
blok verlaat. Besturingselement kan het try
blok verlaten als gevolg van
- normale uitvoering,
- uitvoering van een jump-instructie (dat wil zeggen,
return
break
, ofcontinue
) ofgoto
- doorgifte van een uitzondering buiten het
try
blok.
In het volgende voorbeeld wordt het finally
blok gebruikt om de status van een object opnieuw in te stellen voordat het besturingselement de methode verlaat:
public async Task HandleRequest(int itemId, CancellationToken ct)
{
Busy = true;
try
{
await ProcessAsync(itemId, ct);
}
finally
{
Busy = false;
}
}
U kunt het finally
blok ook gebruiken om toegewezen resources op te schonen die in het try
blok worden gebruikt.
Notitie
Wanneer het type van een resource de IDisposable of interface implementeert, kunt u de using
instructieIAsyncDisposable overwegen. De using
instructie zorgt ervoor dat verkregen resources worden verwijderd wanneer het besturingselement de using
instructie verlaat. De compiler transformeert een using
instructie in een try-finally
instructie.
De uitvoering van het finally
blok is afhankelijk van of het besturingssysteem ervoor kiest om een uitzondering tot rust te laten komen. De enige gevallen waarin finally
blokken niet worden uitgevoerd, zijn onmiddellijke beëindiging van een programma. Een dergelijke beëindiging kan bijvoorbeeld optreden vanwege de Environment.FailFast oproep of een OverflowException of InvalidProgramException uitzondering. De meeste besturingssystemen voeren een redelijke opschoonbewerking van resources uit als onderdeel van het stoppen en lossen van het proces.
De try-catch-finally
instructie
U gebruikt een try-catch-finally
instructie voor het afhandelen van uitzonderingen die kunnen optreden tijdens het uitvoeren van het try
blok en het opgeven van de code die moet worden uitgevoerd wanneer het besturingselement de try
instructie verlaat:
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;
}
}
Wanneer een uitzondering wordt verwerkt door een catch
blok, wordt het finally
blok uitgevoerd na uitvoering van dat catch
blok (zelfs als er een andere uitzondering optreedt tijdens de uitvoering van het catch
blok). Zie respectievelijk de try-catch
instructies en de try-finally
instructiesecties voor meer informatie over catch
en finally
blokken.
C#-taalspecificatie
Zie de volgende secties van de C#-taalspecificatie voor meer informatie: