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 ArgumentOutOfRangeException InvalidOperationExceptiona . 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ével foglalkozó 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ömbargs
ü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
,continue
vagy ), vagygoto
- 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
.IAsyncDisposable IDisposable 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
.
A blokk végrehajtása finally
attól függ, hogy az operációs rendszer kivált-e kivétel-feloldási műveletet. 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 OverflowException InvalidProgramException 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: