Quels types d'exceptions doivent être levés pour les paramètres non valides ou inattendus dans .NET? Quand est-ce que je choisirais un au lieu d'un autre?
Quelle exception utiliseriez-vous si vous avez une fonction qui attend un entier correspondant à un mois et que vous avez passé dans '42'? Est-ce que cela tomberait dans la catégorie "hors gamme" même s'il ne s'agit pas d'une collection?
J'aime utiliser: ArgumentException
, ArgumentNullException
et ArgumentOutOfRangeException
.
ArgumentException
- Quelque chose ne va pas dans l'argument.ArgumentNullException
- L'argument est nul.ArgumentOutOfRangeException
- Je l’utilise peu, mais une utilisation courante est l’indexation dans une collection et la création d’un index trop grand.Il y a aussi d'autres options qui ne se concentrent pas tellement sur l'argument lui-même, mais plutôt jugent l'appel dans son ensemble:
InvalidOperationException
- L'argument peut être correct, mais pas dans l'état actuel de l'objet. Le crédit va à STW (auparavant Yoooder). Votez sa réponse également.NotSupportedException
- Les arguments transmis sont valides, mais ne sont tout simplement pas supportés dans cette implémentation. Imaginez un client FTP et passez une commande que le client ne prend pas en charge.L'astuce consiste à lancer l'exception qui exprime le mieux la raison pour laquelle la méthode ne peut pas être appelée telle quelle. Idéalement, l'exception devrait être détaillée sur ce qui s'est mal passé, pourquoi c'est faux et comment le réparer.
J'aime quand les messages d'erreur pointent vers de l'aide, de la documentation ou d'autres ressources. Par exemple, Microsoft a fait un bon premier pas avec ses articles de base de connaissances, par exemple. "Pourquoi reçois-je un message d'erreur" Opération abandonnée "lorsque je visite une page Web dans Internet Explorer?" . Lorsque vous rencontrez l'erreur, ils vous dirigent vers l'article de la base de connaissances dans le message d'erreur. Ce qu’ils ne font pas bien, c’est qu’ils ne vous disent pas pourquoi c’est un échec.
Merci encore à STW (ex Yoooder) pour les commentaires.
En réponse à votre suivi, je lancerais un ArgumentOutOfRangeException
. Regardez ce que MSDN dit à propos de cette exception:
ArgumentOutOfRangeException
est renvoyé lorsqu'une méthode est appelée et qu'au moins un des arguments transmis à la méthode n'est pas une référence null (Nothing
en Visual Basic) et ne contient pas de valeur valide.
Ainsi, dans ce cas, vous transmettez une valeur, mais ce n'est pas une valeur valide, car votre plage est comprise entre 1 et 12. Cependant, la façon dont vous la documentez montre clairement ce que votre API renvoie. Parce que bien que je puisse dire ArgumentOutOfRangeException
, un autre développeur pourrait dire ArgumentException
. Facilitez-vous et documentez le comportement.
J'ai voté pour réponse de Josh , mais j'aimerais en ajouter un de plus à la liste:
System.InvalidOperationException doit être levé si l'argument est valide, mais l'objet est dans un état où l'argument ne doit pas être utilisé.
Update extrait de MSDN:
InvalidOperationException est utilisé dans les cas où l'échec d'invocation d'une méthode est dû à des raisons autres que des arguments non valides.
Supposons que votre objet dispose d'une méthode PerformAction (action enmSomeAction), enmSomeActions valides sont Open et Close. Si vous appelez PerformAction (enmSomeAction.Open) deux fois de suite, le second appel lève alors l'exception InvalidOperationException (puisque l'argument était valide, mais pas pour l'état actuel du contrôle).
Puisque vous faites déjà le bon choix en programmant de manière défensive, j'ai une autre exception à mentionner: ObjectDisposedException. Si votre objet implémente IDisposable, vous devriez toujours avoir une variable de classe suivi de l'état supprimé. si votre objet a été supprimé et qu'une méthode est appelée, vous devez déclencher l'exception ObjectDisposedException:
public void SomeMethod()
{
If (m_Disposed) {
throw new ObjectDisposedException("Object has been disposed")
}
// ... Normal execution code
}
Mise à jour: Pour répondre à votre question suivante: il s’agit d’une situation un peu ambiguë, compliquée un peu plus par un générique (pas dans le Sens .NET Generics) utilisé pour représenter un ensemble spécifique de données; une énumération ou un autre objet fortement typé conviendrait mieux - mais nous n'avons pas toujours ce contrôle.
Je me pencherais personnellement vers l'exception ArgumentOutOfRangeException et fournirais un message indiquant que les valeurs valides sont comprises entre 1 et 12. Mon raisonnement est que, lorsque vous parlez de mois, en supposant que toutes les représentations entières de mois sont valides, vous vous attendez à une valeur comprise entre 1 et 12. Si seuls certains mois (comme les mois de 31 jours) étaient valides, vous ne traiteriez pas une plage en soi et jetterais une exception générique ArgumentException indiquant les valeurs valides, que je documenterais également dans les commentaires de la méthode.
Selon la valeur réelle et quelle exception convient le mieux:
ArgumentException
(quelque chose ne va pas avec la valeur)
ArgumentNullException
(l'argument est null alors que cela n'est pas autorisé)
ArgumentOutOfRangeException
(l'argument a une valeur en dehors de la plage valide)
Si ce n'est pas assez précis, dérivez simplement votre propre classe d'exceptions de ArgumentException
.
La réponse de Yoooder m'a éclairé. Une entrée est non valide si elle n’est valide à aucun moment, alors qu’une entrée est inattendue s'il n'est pas valide pour l'état actuel du système. Donc, dans le dernier cas, un InvalidOperationException
est un choix raisonnable.
ArgumentException est levée lorsqu'une méthode est appelée et qu'au moins un des arguments transmis ne correspond pas à la spécification de paramètre de la méthode appelée. Toutes les instances de ArgumentException doivent porter un message d'erreur explicite décrivant l'argument non valide, ainsi que la plage attendue de valeurs pour l'argument.
Il existe également quelques sous-classes pour des types spécifiques d'invalidité. Le lien contient des résumés des sous-types et du moment où ils doivent s'appliquer.
Réponse courte:
Ni
Réponse plus longue:
using Argument * Exception (sauf dans une bibliothèque qui est un produit, telle qu'une bibliothèque de composants) est une odeur. Les exceptions concernent les situations exceptionnelles, pas les bogues, ni les manques de l'utilisateur (c'est-à-dire le consommateur d'API).
Réponse la plus longue:
Lancer des exceptions pour des arguments non valides est impoli, sauf si vous écrivez une bibliothèque.
Je préfère utiliser des assertions, pour deux raisons (ou plus):
Voici à quoi ressemble le traitement de l'exception null (étant sarcastique, évidemment):
try {
library.Method(null);
}
catch (ArgumentNullException e) {
// retry with real argument this time
library.Method(realArgument);
}
Les exceptions doivent être utilisées lorsque la situation est attendue mais exceptionnelle (des événements se produisent qui sont indépendants de la volonté du consommateur, tels que la défaillance de IO). Argument * Exception est une indication d'un bogue et doit (à mon avis) être traité avec des tests et assisté avec Debug.Assert
BTW: Dans ce cas particulier, vous auriez pu utiliser le type Month au lieu de int. C # manque de précision en ce qui concerne la sécurité (Aspect # rulez!), Mais vous pouvez parfois empêcher (ou attraper au moment de la compilation) tous ces bogues.
Et oui, Microsoft a tort à ce sujet.
Il existe une exception standard ArgumentException que vous pouvez utiliser ou vous pouvez créer une sous-classe et créer la vôtre. Il existe plusieurs classes ArgumentException spécifiques:
http://msdn.Microsoft.com/en-us/library/system.argumentexception (VS.71) .aspx
Celui qui fonctionne le mieux.