Gestion des exceptions (Guide de programmation C#)
Un bloc try est utilisé par les programmeurs C# pour partitionner du code susceptible d’être affecté par une exception. Des blocs catch associés sont utilisés pour gérer les exceptions générées. Un bloc finally contient du code qui s’exécute dans tous les cas, qu’une exception soit levée ou non dans le bloc try
(il peut s’agir par exemple de la libération des ressources allouées dans le bloc try
). Un bloc try
doit être associé à un ou plusieurs blocs catch
, à un bloc finally
, ou aux deux.
Les exemples suivants montrent une instruction try-catch
, une instruction try-finally
et une instruction try-catch-finally
.
try
{
// Code to try goes here.
}
catch (SomeSpecificException ex)
{
// Code to handle the exception goes here.
// Only catch exceptions that you know how to handle.
// Never catch base class System.Exception without
// rethrowing it at the end of the catch block.
}
try
{
// Code to try goes here.
}
finally
{
// Code to execute after the try block goes here.
}
try
{
// Code to try goes here.
}
catch (SomeSpecificException ex)
{
// Code to handle the exception goes here.
}
finally
{
// Code to execute after the try (and possibly catch) blocks
// goes here.
}
Un bloc try
sans bloc catch
ou finally
provoque une erreur du compilateur.
Blocs catch
Un bloc catch
peut spécifier le type d’exception à intercepter. La spécification de type est appelée filtre d’exception. Le type d’exception doit être dérivé de Exception. En général, ne spécifiez pas Exception comme filtre d’exception sauf si vous savez comment gérer toutes les exceptions susceptibles d’être levées dans le bloc try
ou si vous avez inclus une instruction throw
à la fin de votre bloc catch
.
Vous pouvez chaîner plusieurs blocs catch
avec des classes d’exception différents. Les blocs catch
sont évalués de haut en bas dans votre code, mais un seul bloc catch
est exécuté pour chaque exception levée. Le premier bloc catch
qui spécifie le type exact ou une classe de base de l’exception levée est exécuté. Si aucun bloc catch
ne spécifie une classe d’exception correspondant, un bloc catch
qui n’a pas de type est sélectionné, s’il y en a un dans l’instruction. Lors du positionnement des blocs catch
, il est important de placer en premier les classes d’exception les plus spécifiques (c’est-à-dire les plus dérivés).
Interceptez les exceptions quand les conditions suivantes sont remplies :
- Vous savez pourquoi l’exception a été levée et vous pouvez implémenter une récupération spécifique, par exemple inviter l’utilisateur à entrer un nouveau nom de fichier quand vous interceptez un objet FileNotFoundException.
- Vous pouvez créer et lever une nouvelle exception plus spécifique.
int GetInt(int[] array, int index) { try { return array[index]; } catch (IndexOutOfRangeException e) { throw new ArgumentOutOfRangeException( "Parameter index is out of range.", e); } }
- Vous voulez traiter partiellement une exception avant de la transmettre en vue d’un traitement supplémentaire. Dans l’exemple suivant, un bloc
catch
est utilisé pour ajouter une entrée à un journal d’erreurs avant de relever l’exception.try { // Try to access a resource. } catch (UnauthorizedAccessException e) { // Call a custom error logging procedure. LogError(e); // Re-throw the error. throw; }
Vous pouvez également indiquer des filtres d’exception pour ajouter une expression booléenne à une clause catch. Les filtres d’exception indiquent qu’une clause catch spécifique ne correspond que lorsque cette condition est vraie. Dans l’exemple suivant, les deux clauses catch utilisent la même classe d’exception, mais une condition supplémentaire est vérifiée pour créer un message d’erreur différent :
int GetInt(int[] array, int index)
{
try
{
return array[index];
}
catch (IndexOutOfRangeException e) when (index < 0)
{
throw new ArgumentOutOfRangeException(
"Parameter index cannot be negative.", e);
}
catch (IndexOutOfRangeException e)
{
throw new ArgumentOutOfRangeException(
"Parameter index cannot be greater than the array size.", e);
}
}
Un filtre d’exception qui retourne toujours false
peut être utilisé pour examiner toutes les exceptions sans les traiter. Une pratique courante consiste à enregistrer les exceptions :
public class ExceptionFilter
{
public static void Main()
{
try
{
string? s = null;
Console.WriteLine(s.Length);
}
catch (Exception e) when (LogException(e))
{
}
Console.WriteLine("Exception must have been handled");
}
private static bool LogException(Exception e)
{
Console.WriteLine($"\tIn the log routine. Caught {e.GetType()}");
Console.WriteLine($"\tMessage: {e.Message}");
return false;
}
}
La méthode LogException
retourne toujours false
, aucune clause catch
utilisant ce filtre d’exception ne correspond. La clause catch peut être générale et utiliser System.Exception. Les clauses ultérieures peuvent traiter des classes d’exception plus spécifiques.
Blocs Finally
Un bloc finally
vous permet de nettoyer les actions qui sont exécutées dans un bloc try
. S’il est présent, le bloc finally
s’exécute en dernier, après le bloc try
et tout bloc catch
mis en correspondance. Un bloc finally
s’exécute toujours, qu’une exception soit levée ou non ou même si aucun bloc catch
correspondant au type d’exception n’est trouvé.
Le bloc finally
peut être utilisé pour libérer des ressources telles que des flux de fichiers, des connexions de base de données et des handles graphiques, sans attendre que le récupérateur de mémoire dans le runtime finalise les objets.
Dans l’exemple suivant, le bloc finally
est utilisé pour fermer un fichier ouvert dans le bloc try
. Notez que l’état du handle de fichier est vérifié avant la fermeture du fichier. Si le bloc try
ne peut pas ouvrir le fichier, le handle de fichier a encore la valeur null
et le bloc finally
n’essaie pas de le fermer. Au lieu de cela, si le fichier est ouvert avec succès dans le bloc try
, le bloc finally
ferme le fichier ouvert.
FileStream? file = null;
FileInfo fileinfo = new System.IO.FileInfo("./file.txt");
try
{
file = fileinfo.OpenWrite();
file.WriteByte(0xF);
}
finally
{
// Check for null because OpenWrite might have failed.
file?.Close();
}
Spécification du langage C#
Pour plus d’informations, consultez Exceptions et Instruction try dans la spécification du langage C#. La spécification du langage est la source de référence pour la syntaxe C# et son utilisation.