Examiner comment créer et lever des exceptions en C#
- 16 minutes
.NET fournit une hiérarchie de classes d’exception qui dérivent de la System.Exception classe de base. Les applications C# peuvent créer et lever des exceptions de n’importe quel type d’exception. Les développeurs peuvent également personnaliser des objets d’exception avec des informations spécifiques à l’application en affectant des valeurs de propriété.
Remarque
Ce module se concentre sur la création et la levée d’exceptions et la personnalisation d’objets d’exception. La création de classes d’exceptions personnalisées est en dehors de l’étendue de ce module.
Créer un objet d’exception
La création et la levée d’exceptions à partir de votre code constituent un aspect important de la programmation C#. La possibilité de générer une exception en réponse à une condition, un problème ou une erreur spécifique vous permet de garantir la stabilité de votre application.
Le type d’exception que vous créez dépend du problème de codage et doit correspondre à l’objectif prévu de l’exception aussi étroitement que possible.
Par exemple, supposons que vous créez une méthode nommée GraphData qui effectue une analyse des données. La méthode reçoit un tableau de données en tant que paramètre d’entrée. La méthode s’attend à ce que les données d’entrée se situent dans une plage particulière. Si la méthode reçoit des données qui se trouvent en dehors de la plage attendue, elle crée et lève une exception de type ArgumentException. L’exception est gérée quelque part dans la pile des appels par le code chargé de fournir les données.
Voici quelques types d’exceptions courants que vous pouvez utiliser lors de la création d’une exception :
ArgumentExceptionouArgumentNullException: utilisez ces types d’exceptions lorsqu’une méthode ou un constructeur est appelé avec une valeur d’argument ou une référence Null non valide.InvalidOperationException: utilisez ce type d’exception lorsque les conditions d’exploitation d’une méthode ne prennent pas en charge la réussite d’un appel de méthode particulier.NotSupportedException: Utilisez ce type d’exception lorsqu’une opération ou une fonctionnalité n’est pas prise en charge.IOException: Utilisez ce type d’exception lorsqu’une opération d’entrée/sortie échoue.FormatException: utilisez ce type d’exception lorsque le format d’une chaîne ou de données est incorrect.
Le new mot clé est utilisé pour créer une instance d’une exception. Par exemple, vous pouvez créer une instance du type d’exception ArgumentException comme suit :
ArgumentException invalidArgumentException = new ArgumentException();
Configurer et lever des exceptions personnalisées
Le processus de levée d’un objet d’exception implique la création d’une instance d’une classe dérivée d’exception, la configuration facultative des propriétés de l’exception, puis la levée de l’objet à l’aide du throw mot clé.
Il est souvent utile de personnaliser une exception avec des informations contextuelles avant de la lever. Vous pouvez fournir des informations spécifiques à l’application dans un objet d’exception en configurant ses propriétés. Par exemple, le code suivant crée un objet d’exception nommé invalidArgumentException avec une propriété personnalisée Message , puis lève l’exception :
ArgumentException invalidArgumentException = new ArgumentException("ArgumentException: The 'GraphData' method received data outside the expected range.");
throw invalidArgumentException;
Remarque
La propriété Message d’une exception est en lecture seule. Par conséquent, une propriété personnalisée Message doit être définie lors de l’instanciation de l’objet.
Lors de la personnalisation d’un objet d’exception, il est important de fournir des messages d’erreur clairs qui décrivent le problème et comment le résoudre. Vous pouvez également inclure des informations supplémentaires telles que des traces de pile et des codes d’erreur pour aider les utilisateurs à corriger le problème.
Un objet d’exception peut également être créé directement dans une throw instruction. Par exemple:
throw new FormatException("FormatException: Calculations in process XYZ have been cancelled due to invalid data format.");
Voici quelques considérations à prendre en compte lors de la levée d’une exception :
- La propriété
Messagedoit expliquer la raison de l’exception. Toutefois, les informations sensibles ou qui représentent un problème de sécurité ne doivent pas être placées dans le texte du message. - La
StackTracepropriété est souvent utilisée pour suivre l’origine de l’exception. Cette propriété de chaîne contient le nom des méthodes sur la pile des appels en cours, ainsi que le nom de fichier et le numéro de ligne dans chaque méthode associée à l’exception. UnStackTraceobjet est créé automatiquement par le Common Language Runtime (CLR) à partir du point de l’instructionthrow. Les exceptions doivent être levées à partir du point où la trace de pile doit commencer.
Quand lever une exception
Les méthodes doivent lever une exception chaque fois qu’elles ne peuvent pas accomplir leur objectif prévu. L’exception levée doit être basée sur l’exception la plus spécifique disponible qui correspond aux conditions d’erreur.
Envisagez un scénario dans lequel un développeur travaille sur une application qui implémente un processus métier. Le processus métier dépend de l’entrée utilisateur. Si l’entrée ne correspond pas au type de données attendu, la méthode qui implémente le processus métier crée et lève une exception. L’objet d’exception peut être configuré avec des informations spécifiques à l’application dans les valeurs de propriété. L’exemple de code suivant illustre le scénario :
string[][] userEnteredValues = new string[][]
{
new string[] { "1", "two", "3"},
new string[] { "0", "1", "2"}
};
foreach (string[] userEntries in userEnteredValues)
{
try
{
BusinessProcess1(userEntries);
}
catch (Exception ex)
{
if (ex.StackTrace.Contains("BusinessProcess1") && (ex is FormatException))
{
Console.WriteLine(ex.Message);
}
}
}
static void BusinessProcess1(string[] userEntries)
{
int valueEntered;
foreach (string userValue in userEntries)
{
try
{
valueEntered = int.Parse(userValue);
// completes required calculations based on userValue
// ...
}
catch (FormatException)
{
FormatException invalidFormatException = new FormatException("FormatException: User input values in 'BusinessProcess1' must be valid integers");
throw invalidFormatException;
}
}
}
Dans cet exemple de code, les instructions de niveau supérieur appellent la BusinessProcess1 méthode, en passant un tableau de chaînes contenant des valeurs entrées par l’utilisateur. La BusinessProcess1 méthode attend des valeurs d’entrée utilisateur qui peuvent être converties en entier. Lorsque la méthode rencontre des données avec un format non valide, elle crée une instance du type d’exception FormatException à l’aide d’une propriété personnalisée Message. La méthode lève ensuite l’exception. L’exception est interceptée dans les instructions de niveau supérieur en tant qu’objet nommé ex. Les propriétés de l’objet ex sont examinées avant d’afficher le message d’exception à l’utilisateur. Tout d’abord, le code examine la StackTrace propriété pour voir si elle contient « BusinessProcess1 ». Deuxièmement, l’objet ex d’exception est vérifié comme étant de type FormatException.
Levée répétée d’exceptions
Outre la levée d'une nouvelle exception, throw peut être utilisé pour relancer une exception dans un bloc de code catch. Dans ce cas, throw ne prend pas d’opérande d’exception.
catch (Exception ex)
{
// handle or partially handle the exception
// ...
// re-throw the original exception object for further handling down the call stack
throw;
}
Lorsque vous lèvez à nouveau une exception, l’objet d’exception d’origine est utilisé. Vous ne perdez donc aucune information sur l’exception. Si vous souhaitez créer un objet d’exception qui encapsule l’exception d’origine, vous pouvez passer l’exception d’origine en tant qu’argument au constructeur d’un nouvel objet d’exception. Par exemple:
catch (Exception ex)
{
// handle or partially handle the exception
// ...
// create a new exception object that wraps the original exception
throw new ApplicationException("An error occurred", ex);
}
Pour le scénario d’application « BusinessProcess1 », tenez compte des mises à jour suivantes :
- La
BusinessProcess1méthode a été mise à jour pour inclure des détails supplémentaires.BusinessProcess1rencontre maintenant deux problèmes et doit générer des exceptions pour chaque problème. - Les déclarations de niveau supérieur ont été mises à jour. Les instructions de niveau supérieur appellent maintenant la
OperatingProcedure1méthode.OperatingProcedure1appelleBusinessProcess1dans untrybloc de code. - La
OperatingProcedure1méthode est en mesure de gérer l’un des types d’exceptions et de gérer partiellement l’autre. Une fois l’exception partiellement gérée,OperatingProcedure1doit lever à nouveau l’exception d’origine.
L’exemple de code suivant illustre le scénario mis à jour :
try
{
OperatingProcedure1();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine("Exiting application.");
}
static void OperatingProcedure1()
{
string[][] userEnteredValues = new string[][]
{
new string[] { "1", "two", "3"},
new string[] { "0", "1", "2"}
};
foreach(string[] userEntries in userEnteredValues)
{
try
{
BusinessProcess1(userEntries);
}
catch (Exception ex)
{
if (ex.StackTrace.Contains("BusinessProcess1"))
{
if (ex is FormatException)
{
Console.WriteLine(ex.Message);
Console.WriteLine("Corrective action taken in OperatingProcedure1");
}
else if (ex is DivideByZeroException)
{
Console.WriteLine(ex.Message);
Console.WriteLine("Partial correction in OperatingProcedure1 - further action required");
// re-throw the original exception
throw;
}
else
{
// create a new exception object that wraps the original exception
throw new ApplicationException("An error occurred - ", ex);
}
}
}
}
}
static void BusinessProcess1(string[] userEntries)
{
int valueEntered;
foreach (string userValue in userEntries)
{
try
{
valueEntered = int.Parse(userValue);
checked
{
int calculatedValue = 4 / valueEntered;
}
}
catch (FormatException)
{
FormatException invalidFormatException = new FormatException("FormatException: User input values in 'BusinessProcess1' must be valid integers");
throw invalidFormatException;
}
catch (DivideByZeroException)
{
DivideByZeroException unexpectedDivideByZeroException = new DivideByZeroException("DivideByZeroException: Calculation in 'BusinessProcess1' encountered an unexpected divide by zero");
throw unexpectedDivideByZeroException;
}
}
}
L’exemple de code mis à jour produit la sortie suivante :
FormatException: User input values in 'BusinessProcess1' must be valid integers
Corrective action taken in OperatingProcedure1
DivideByZeroException: Calculation in 'BusinessProcess1' encountered an unexpected divide by zero
Partial correction in OperatingProcedure1 - further action required
DivideByZeroException: Calculation in 'BusinessProcess1' encountered an unexpected divide by zero
Exiting application.
Éléments à éviter lors de la levée d’exceptions
La liste suivante identifie les pratiques à éviter lors de la levée d’exceptions :
- N’utilisez pas d’exceptions pour modifier le flux d’un programme dans le cadre de l’exécution ordinaire. Utilisez des exceptions pour signaler et gérer les conditions d’erreur.
- Les exceptions ne doivent pas être retournées en tant que valeur ou paramètre de retour au lieu d’être levées.
- Ne lancez pas intentionnellement
System.Exception,System.SystemException,System.NullReferenceExceptionouSystem.IndexOutOfRangeExceptiondepuis votre propre code source. - Ne créez pas d’exceptions qui peuvent être levées en mode débogage, mais pas en mode mise en production. Pour identifier les erreurs d’exécution pendant la phase de développement, utilisez
Debug.Assertà la place.
Remarque
La Debug.Assert méthode est un outil permettant d’intercepter les erreurs logiques pendant le développement. Par défaut, la Debug.Assert méthode fonctionne uniquement dans les builds de débogage. Vous pouvez utiliser Debug.Assert dans les sessions de débogage pour rechercher une condition qui ne doit jamais se produire. La méthode prend deux paramètres : une condition booléenne à vérifier et un message de chaîne facultatif à afficher si la condition est false. Debug.Assert ne doit pas être utilisé à la place de la levée d’une exception, qui est un moyen de gérer des situations exceptionnelles pendant l’exécution normale de votre code. Vous devez utiliser Debug.Assert pour intercepter les erreurs qui ne doivent jamais se produire et utiliser des exceptions pour gérer les erreurs qui peuvent se produire pendant l’exécution normale de votre programme.
Récapitulatif
Voici quelques points importants à retenir de cette unité :
- Lors de la création et de la levée d’une exception, le type d’exception doit correspondre à l’objectif prévu de l’exception aussi étroitement que possible.
- Pour
throwune exception, vous créez une instance d’une classe dérivée d’exception, configurez ses propriétés, puis utilisez le mot cléthrow. - Lors de la création d’un objet d’exception, il est important de fournir des messages d’erreur clairs et des informations supplémentaires pour aider les utilisateurs à corriger le problème.