Instructions de gestion des exceptions : throw
, try-catch
, try-finally
et try-catch-finally
Vous utilisez les instructions throw
et try
pour travailler avec des exceptions. Utilisez l’instruction throw
pour lever une exception. Utilisez l’instruction try
pour intercepter et gérer les exceptions qui peuvent se produire pendant l’exécution d’un bloc de code.
Instruction throw
L’instruction throw
lève une exception :
if (shapeAmount <= 0)
{
throw new ArgumentOutOfRangeException(nameof(shapeAmount), "Amount of shapes must be positive.");
}
Dans une instruction throw e;
, le résultat de l’expression e
doit être implicitement convertible en System.Exception.
Vous pouvez utiliser les classes d’exception intégrées, par exemple ArgumentOutOfRangeException ou InvalidOperationException. .NET fournit également les méthodes d’assistance suivantes pour lever des exceptions dans certaines conditions : ArgumentNullException.ThrowIfNull et ArgumentException.ThrowIfNullOrEmpty. Vous pouvez également définir vos propres classes d’exception qui dérivent de System.Exception. Pour plus d’informations, consultez Création et levée d’exceptions.
À l’intérieur d’un bloc catch
, vous pouvez utiliser une instruction throw;
pour lever à nouveau l’exception gérée par le bloc catch
:
try
{
ProcessShapes(shapeAmount);
}
catch (Exception e)
{
LogError(e, "Shape processing failed.");
throw;
}
Notes
throw;
conserve la trace de pile d’origine de l’exception, qui est stockée dans la propriété Exception.StackTrace. À l’inverse, throw e;
met à jour la propriété StackTrace de e
.
Quand une exception est levée, le Common Language Runtime (CLR) recherche le bloc catch
qui gère cette exception. Si la méthode exécutée ne contient pas un tel bloc catch
, le CLR examine la méthode qui a appelé la méthode actuelle, puis remonte la pile des appels. Si aucun bloc catch
n’est trouvé, le CLR met fin au thread en cours d’exécution. Pour plus d’informations, consultez la section Comment les exceptions sont gérées de la spécification du langage C#.
Expression throw
Vous pouvez également utiliser throw
comme expression. Cela peut être pratique dans un certain nombre de cas, notamment :
l’opérateur conditionnel : L’exemple suivant utilise une expression
throw
pour lever un ArgumentException lorsque le tableauargs
passé est vide :string first = args.Length >= 1 ? args[0] : throw new ArgumentException("Please supply at least one argument.");
l’opérateur de fusion de Null : L’exemple suivant utilise une expression
throw
pour lever un ArgumentNullException lorsque la chaîne à affecter à une propriété estnull
:public string Name { get => name; set => name = value ?? throw new ArgumentNullException(paramName: nameof(value), message: "Name cannot be null"); }
un lambda ou une méthode expression-bodied : L’exemple suivant utilise une expression
throw
pour lever un InvalidCastException pour indiquer qu’une conversion en valeur DateTime n’est pas prise en charge :DateTime ToDateTime(IFormatProvider provider) => throw new InvalidCastException("Conversion to a DateTime is not supported.");
Instruction try
Vous pouvez utiliser l’instruction try
sous l’une des formes suivantes : try-catch
- pour gérer les exceptions qui peuvent se produire pendant l’exécution du code à l’intérieur d’un bloc try
, try-finally
- pour spécifier le code qui est exécuté lorsque le contrôle quitte le bloc try
et try-catch-finally
- en combinaison des deux formulaires précédents.
Instruction try-catch
Utilisez l’instruction try-catch
pour intercepter et gérer les exceptions qui peuvent se produire pendant l’exécution d’un bloc de code. Placez le code là où une exception peut se produire à l’intérieur d’un bloc try
. Utilisez une clause catch pour spécifier le type de base des exceptions que vous souhaitez gérer dans le bloc catch
correspondant :
try
{
var result = Process(-3, 4);
Console.WriteLine($"Processing succeeded: {result}");
}
catch (ArgumentException e)
{
Console.WriteLine($"Processing failed: {e.Message}");
}
Vous pouvez fournir plusieurs clauses catch :
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.");
}
Lorsqu’une exception se produit, les clauses catch sont examinées dans l’ordre spécifié, de haut en bas. Au maximum, un seul bloc catch
est exécuté pour toute exception levée. Comme le montre également l’exemple précédent, vous pouvez omettre la déclaration d’une variable d’exception et spécifier uniquement le type d’exception dans une clause catch. Une clause catch sans type d’exception spécifié correspond à n’importe quelle exception et, le cas échéant, doit être la dernière clause catch.
Si vous souhaitez lever à nouveau une exception interceptée, utilisez l’instruction throw
, comme dans l’exemple suivant :
try
{
var result = Process(-3, 4);
Console.WriteLine($"Processing succeeded: {result}");
}
catch (Exception e)
{
LogError(e, "Processing failed.");
throw;
}
Notes
throw;
conserve la trace de pile d’origine de l’exception, qui est stockée dans la propriété Exception.StackTrace. À l’inverse, throw e;
met à jour la propriété StackTrace de e
.
Un filtre d’exception when
En plus d’un type d’exception, vous pouvez également spécifier un filtre d’exception qui examine plus en détail une exception et détermine si le bloc catch
correspondant gère cette exception. Un filtre d’exception est une expression booléenne qui suit le mot clé when
, comme dans l’exemple suivant :
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}");
}
L’exemple précédent utilise un filtre d’exception pour fournir un seul bloc catch
pour gérer les exceptions de deux types spécifiés.
Vous pouvez fournir plusieurs clauses catch
pour le même type d’exception si elles se distinguent par des filtres d’exception. L’une de ces clauses peut ne pas avoir de filtre d’exception. Si une telle clause existe, il doit s’agir de la dernière des clauses qui spécifient ce type d’exception.
Si une clause catch
a un filtre d’exception, elle peut spécifier le type d’exception qui est le même ou moins dérivé qu’un type d’exception d’une clause catch
qui apparaît après elle. Par exemple, si un filtre d’exception est présent, une clause catch (Exception e)
n’a pas besoin d’être la dernière clause.
Exceptions dans les méthodes asynchrones et itérateurs
Si une exception se produit dans une fonction asynchrone, elle se propage à l’appelant de la fonction lorsque vous attendez le résultat de la fonction, comme le montre l’exemple suivant :
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;
}
Si une exception se produit dans une méthode d’itérateur, elle se propage à l’appelant uniquement lorsque l’itérateur passe à l’élément suivant.
Instruction try-finally
Dans une instruction try-finally
, le bloc finally
est exécuté lorsque le contrôle quitte le bloc try
. Le contrôle peut quitter le bloc try
à la suite de
- une exécution normale,
- l’exécution d’une instruction de saut (c’est-à-dire,
return
,break
,continue
ougoto
), ou - la propagation d’une exception hors du bloc
try
.
L’exemple suivant utilise le bloc finally
pour réinitialiser l’état d’un objet avant que le contrôle quitte la méthode :
public async Task HandleRequest(int itemId, CancellationToken ct)
{
Busy = true;
try
{
await ProcessAsync(itemId, ct);
}
finally
{
Busy = false;
}
}
Vous pouvez également utiliser le bloc finally
pour nettoyer les ressources allouées utilisées dans le bloc try
.
Notes
Lorsque le type d’une ressource implémente l’interface IDisposable ou IAsyncDisposable, considérez l’instruction using
. L’instruction using
garantit que les ressources acquises sont supprimées lorsque le contrôle quitte l’instruction using
. Le compilateur transforme une instruction using
en instruction try-finally
.
L’exécution du bloc finally
varie selon que le système d’exploitation choisit de déclencher une opération de déroulement d’exception. Les seuls cas où les blocs finally
ne sont pas exécutés impliquent l’arrêt immédiat d’un programme. Par exemple, un tel arrêt peut se produire en raison de l’appel Environment.FailFast ou d’une exception OverflowException ou InvalidProgramException. La plupart des systèmes d’exploitation effectuent un nettoyage raisonnable des ressources dans le cadre de l’arrêt et du déchargement du processus.
Instruction try-catch-finally
Vous utilisez une instruction try-catch-finally
pour gérer les exceptions qui peuvent se produire pendant l’exécution du bloc try
et pour spécifier le code qui doit être exécuté lorsque le contrôle quitte l’instruction 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;
}
}
Lorsqu’une exception est gérée par un bloc catch
, le bloc finally
est exécuté après l’exécution de ce bloc catch
(même si une autre exception se produit pendant l’exécution du bloc catch
). Pour plus d’informations sur les blocs catch
et finally
, consultez respectivement les sections L’instruction try-catch
et L’instructiontry-finally
.
spécification du langage C#
Pour plus d’informations, consultez les sections suivantes de la spécification du langage C# :