Megosztás a következőn keresztül:


Kivételkezelési utasítások – throw, try-catch, try-finallyés try-catch-finally

A kivételekkel és try az throw utasításokkal dolgozhat. Az utasítás használatával throw kivételt jelezhet. Az utasítással try elfoghatja és kezelheti a kódblokk végrehajtása során esetlegesen előforduló kivételeket.

Az throw utasítás

Az throw utasítás kivételt jelez:

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

throw e; Egy utasításban a kifejezés e eredményének implicit módon konvertálhatónak System.Exceptionkell lennie.

Használhatja például a beépített kivételosztályokat vagy ArgumentOutOfRangeExceptionInvalidOperationExceptiona . A .NET a következő segédmetenek is biztosítja, hogy bizonyos feltételek mellett kivételeket adjon ki: ArgumentNullException.ThrowIfNull és ArgumentException.ThrowIfNullOrEmpty. Saját kivételosztályokat is meghatározhat, amelyekből származik System.Exception. További információ: Kivételek létrehozása és kivetése.

catch Egy blokkon belül egy throw; utasítással újra elvetheti a blokk által kezelt kivételtcatch:

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

Feljegyzés

throw; Megőrzi a kivétel eredeti veremnyomát, amely a Exception.StackTrace tulajdonságban van tárolva. Ezzel ellentétben frissíti throw e; a StackTrace tulajdonságot e.

Kivétel esetén a közös nyelvi futtatókörnyezet (CLR) megkeresi azt a blokkot, amely képes kezelni ezt a catch kivételt. Ha a jelenleg végrehajtott metódus nem tartalmaz ilyen blokkot catch , a CLR megvizsgálja az aktuális metódust nevesítő metódust, és így tovább a hívási vermet. Ha nem catch található blokk, a CLR leállítja a végrehajtó szálat. További információkért tekintse meg a C#-nyelv specifikációjának a kivételek kezelésévelfoglalkozó szakaszát.

A throw kifejezés

Kifejezésként is használható throw . Ez számos esetben kényelmes lehet, például:

  • a feltételes operátor. Az alábbi példa egy throw kifejezést használ, amikor ArgumentException az átadott tömb args üres:

    string first = args.Length >= 1 
        ? args[0]
        : throw new ArgumentException("Please supply at least one argument.");
    
  • a null-szenesítő operátor. Az alábbi példa egy throw kifejezés használatával ad vissza egy ArgumentNullException értéket, amikor a tulajdonsághoz rendelendő sztring a következő null:

    public string Name
    {
        get => name;
        set => name = value ??
            throw new ArgumentNullException(paramName: nameof(value), message: "Name cannot be null");
    }
    
  • kifejezéssel testesített lambda vagy metódus. Az alábbi példa egy throw kifejezéssel InvalidCastException jelzi, hogy az értékké alakítás DateTime nem támogatott:

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

Az try utasítás

Az utasítást a try következő űrlapok bármelyikében használhatja: try-catch - a kód blokkon belüli try végrehajtása során esetlegesen előforduló kivételek kezelésére, try-finally - a blokk elhagyásakor try végrehajtott kód megadására, és try-catch-finally - az előző két űrlap kombinációjaként.

Az try-catch utasítás

Az utasítással kezelheti a try-catch kódblokk végrehajtása során esetlegesen előforduló kivételeket. Helyezze el a kódot, ahol kivétel léphet fel egy blokkon try belül. A fogási záradék használatával adja meg a megfelelő catch blokkban kezelni kívánt kivételek alaptípusát:

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

Több fogási záradékot is megadhat:

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.");
}

Kivétel esetén a fogási záradékokat a rendszer a megadott sorrendben vizsgálja felülről lefelé. Legfeljebb csak egy catch blokk hajtható végre minden kidobott kivétel esetén. Ahogy az előző példa is mutatja, kihagyhatja egy kivételváltozó deklarációját, és csak a kivételtípust adhatja meg egy fogási záradékban. A megadott kivételtípus nélküli fogási záradék minden kivételnek megfelel, és ha van ilyen, akkor az utolsó fogási záradéknak kell lennie.

Ha ismét ki szeretne dobni egy kivételt, használja az throw utasítást, ahogy az alábbi példa is mutatja:

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

Feljegyzés

throw; Megőrzi a kivétel eredeti veremnyomát, amely a Exception.StackTrace tulajdonságban van tárolva. Ezzel ellentétben frissíti throw e; a StackTrace tulajdonságot e.

Kivételszűrő when

A kivételtípus mellett megadhat egy kivételszűrőt is, amely tovább vizsgálja a kivételt, és eldönti, hogy a megfelelő catch blokk kezeli-e ezt a kivételt. A kivételszűrő egy logikai kifejezés, amely a when kulcsszót követi, ahogy az alábbi példa is mutatja:

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}");
}

Az előző példa egy kivételszűrővel egyetlen blokkot catch biztosít két megadott típus kivételeinek kezeléséhez.

Több záradékot is megadhat catch ugyanahhoz a kivételtípushoz, ha kivételszűrőkkel különböztetik meg őket. Előfordulhat, hogy az egyik záradék kivételszűrővel nem rendelkezik. Ha létezik ilyen záradék, akkor a kivételtípust meghatározó záradékok közül az utolsónak kell lennie.

Ha egy catch záradék kivételszűrővel rendelkezik, megadhatja azt a kivételtípust, amely megegyezik a utána megjelenő záradék kivételtípusával catch vagy kevésbé származtatott típusával. Ha például egy kivételszűrő van jelen, catch (Exception e) a záradéknak nem kell az utolsó záradéknak lennie.

Kivételek az aszinkron és iterátor metódusokban

Ha kivétel történik egy aszinkron függvényben, akkor a függvény hívójának propagálja, amikor a függvény eredményére vár , ahogy az alábbi példa is mutatja:

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;
}

Ha egy iterátormetódusban kivétel történik, az csak akkor propagálódik a hívónak, ha az iterátor továbblép a következő elemre.

Az try-finally utasítás

try-finally Egy utasításban a finally blokk akkor lesz végrehajtva, amikor a vezérlő elhagyja a try blokkot. A vezérlő elhagyhatja a try blokkot a következő miatt:

  • normál végrehajtás,
  • ugrási utasítás végrehajtása (vagyis return, , break, continuevagy ), vagy goto
  • kivétel propagálása a try blokkból.

Az alábbi példa egy objektum állapotának visszaállítására használja a finally blokkot, mielőtt a vezérlő elhagyja a metódust:

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

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

A blokk használatával finally a blokkban try használt lefoglalt erőforrásokat is törölheti.

Feljegyzés

Ha egy erőforrás típusa implementálja a felületet, vegye figyelembe az utasítástusing.IAsyncDisposableIDisposable Az using utasítás biztosítja, hogy a beszerzett erőforrások törlődnek, amikor a vezérlő elhagyja az utasítást using . A fordító utasítássá alakít át egy using utasítást try-finally .

Szinte minden esetben finally blokkokat hajtanak végre. Az egyetlen olyan eset, amikor finally a blokkok végrehajtása nem történik meg, a program azonnali leállításával jár. Ilyen megszüntetés történhet például a Environment.FailFast hívás vagy egy OverflowExceptionInvalidProgramException kivétel miatt. A legtöbb operációs rendszer ésszerű erőforrás-tisztítást végez a folyamat leállításának és eltávolításának részeként.

Az try-catch-finally utasítás

Mindkét utasítással try-catch-finally kezelheti a try blokk végrehajtása során esetlegesen előforduló kivételeket, és megadhatja azt a kódot, amelyet akkor kell végrehajtani, amikor a vezérlő elhagyja az utasítást 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;
    }

}

Ha egy catch blokk kivételt kezel, a finally blokk a blokk végrehajtása catch után lesz végrehajtva (még akkor is, ha egy másik kivétel történik a catch blokk végrehajtása során). További információ catch és finally blokkok : Az try-catch utasítás és az try-finally Utasítás szakasz.

C# nyelvspecifikáció

További információt a C# nyelvspecifikációjának alábbi szakaszaiban talál:

Lásd még