Instructies voor het afhandelen van uitzonderingen - throw, try-catch, try-finallyen 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 throwArgumentException expressie te werpen wanneer de doorgegeven matrix args 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 throwArgumentNullException expressie te genereren wanneer de tekenreeks die aan een eigenschap moet worden toegewezen, is null:

    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, returnbreak, of continue) of goto
  • 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.

In bijna alle gevallen finally worden blokken uitgevoerd. 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 detry-catch instructies en detry-finally instructiesecties voor meer informatie over catch en finally blokken.

C#-taalspecificatie

Zie de volgende secties van de C#-taalspecificatie voor meer informatie:

Zie ook