Quelles exceptions doivent être levées pour les parameters non valides ou inattendus dans .NET?

Quels types d’exceptions doivent être générés pour les parameters non valides ou inattendus dans .NET? Quand choisirais-je l’un au lieu de l’autre?

Suivre:

Quelle exception utiliseriez-vous si vous avez une fonction qui attend un nombre entier correspondant à un mois et que vous avez passé ’42’? Serait-ce tomber dans la catégorie “hors de scope” même si ce n’est pas une collection?

J’aime utiliser: ArgumentException , ArgumentNullException et ArgumentOutOfRangeException .

  • ArgumentException – Quelque chose ne va pas avec l’argument.
  • ArgumentNullException – L’argument est null.
  • ArgumentOutOfRangeException – Je ne l’utilise pas beaucoup, mais une utilisation courante consiste à indexer dans une collection et à donner un index trop grand.

Il y a aussi d’autres options qui ne se concentrent pas tant sur l’argument lui-même, mais jugent plutôt l’appel dans son ensemble:

  • InvalidOperationException – L’argument peut être OK, mais pas dans l’état actuel de l’object. Le crédit va à STW (précédemment Yoooder). Votez aussi sa réponse .
  • NotSupportedException – Les arguments transmis sont valides, mais ne sont pas pris en charge dans cette implémentation. Imaginez un client FTP, et vous passez une commande dans laquelle le client ne prend pas en charge.

L’astuce consiste à lancer l’exception qui exprime le mieux pourquoi la méthode ne peut pas être appelée telle quelle. Idéalement, l’exception devrait être détaillée sur ce qui a mal tourné, pourquoi elle est incorrecte et comment y remédier.

J’aime quand les messages d’erreur indiquent de l’aide, de la documentation ou d’autres ressources. Par exemple, Microsoft a fait une bonne première étape avec ses articles sur la base de connaissances, par exemple «Pourquoi est-ce que je reçois un message d’erreur« Opération annulé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 le disent pas, précisément pourquoi cela a échoué.

Merci à STW (ex Yoooder) pour les commentaires.


En réponse à votre suivi, je lancerais une 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.

Donc, 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 manière dont vous la documentez le rend clair, ce que votre API génère. Bien que je puisse dire ArgumentOutOfRangeException , un autre développeur pourrait dire ArgumentException . Rendez-le facile et documentez le comportement.

J’ai voté pour la réponse de Josh , mais j’aimerais en append une à la liste:

System.InvalidOperationException doit être levé si l’argument est valide, mais l’object est dans un état où l’argument ne doit pas être utilisé.

Mise à jour prise à partir de MSDN:

InvalidOperationException est utilisé dans les cas où l’incapacité à invoquer une méthode est provoquée par des raisons autres que des arguments non valides.

Disons que votre object a une méthode PerformAction (action enmSomeAction), que les enmSomeActions valides sont Open et Close. Si vous appelez PerformAction (enmSomeAction.Open) deux fois de suite, le second appel doit lancer l’exception InvalidOperationException (car l’arugment était valide, mais pas pour l’état actuel du contrôle)

Comme vous faites déjà ce qu’il faut en programmant de manière défensive, une autre exception à mentionner est ObjectDisposedException. Si votre object implémente IDisposable, vous devriez toujours avoir une variable de classe pour suivre l’état éliminé; Si votre object a été éliminé et qu’une méthode est appelée, vous devez générer 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 suivi: il s’agit d’une situation un peu ambiguë, rendue un peu plus compliquée par le fait qu’un type de données générique (pas dans le sens des génériques .NET) est utilisé pour représenter un dataset spécifique; un enum ou un autre object fortement typé serait un ajustement plus idéal – mais nous n’avons pas toujours ce contrôle.

Je me pencherais personnellement sur ArgumentOutOfRangeException et fournirais un message indiquant que les valeurs valides sont 1-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 seulement certains mois (comme les mois ayant 31 jours) étaient valides, alors vous ne seriez pas confronté à une plage par se et je lancerais une exception ArgumentException qui indiquerait les valeurs valides, et je les documenterais également dans les commentaires de la méthode.

Selon la valeur réelle et quelle exception correspond le mieux:

  • ArgumentException (quelque chose ne va pas avec la valeur)

  • ArgumentNullException (l’argument est null alors que ce 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’exception à partir d’ ArgumentException .

La réponse de Yoooder m’a éclairé. Une entrée est invalide si elle n’est pas valide à tout moment, alors qu’une entrée est inattendue si elle n’est pas valide pour l’état actuel du système. Donc, dans le dernier cas, une InvalidOperationException est un choix raisonnable.

exception d’argument.

  • System.ArgumentException
  • System.ArgumentNullException
  • System.ArgumentOutOfRangeException

ArgumentException :

ArgumentException est renvoyé lorsqu’une méthode est appelée et qu’au moins un des arguments transmis ne répond pas à la spécification de paramètre de la méthode appelée. Toutes les instances de ArgumentException doivent porter un message d’erreur significatif décrivant l’argument non valide, ainsi que la plage de valeurs attendue pour l’argument.

Quelques sous-classes existent également pour des types d’invalidité spécifiques. Le lien contient des résumés des sous-types et leur date d’application.

Réponse courte:
Ni

Réponse plus longue:
using Argument * Exception (sauf dans une bibliothèque qui est un produit sur son, comme la bibliothèque de composants) est une odeur. Les exceptions consistent à gérer les situations exceptionnelles, et non les bogues, et non les défaillances des utilisateurs (à savoir les consommateurs d’API).

La plus longue réponse:
Lancer des exceptions pour des arguments invalides est impoli, sauf si vous écrivez une bibliothèque.
Je préfère utiliser des assertions pour deux raisons (ou plus):

  • Les assertions n’ont pas besoin d’être testées, alors que les assertions font, et les tests avec ArgumentNullException sont ridicules (essayez-les).
  • Les assertions communiquent mieux l’utilisation prévue de l’unité et sont plus proches de la documentation exécutable qu’une spécification de comportement de classe.
  • Vous pouvez modifier le comportement de violation d’assertion. Par exemple, dans la compilation de débogage, une boîte de message est correcte, de sorte que votre assurance qualité vous basha immédiatement (vous obtenez également votre IDE sur la ligne où il se produit), tandis que dans le test unitaire, vous pouvez indiquer un échec .

Voici à quoi ressemble la gestion des exceptions null (sarcastique, évidemment):

 try { library.Method(null); } catch (ArgumentNullException e) { // retry with real argument this time library.Method(realArgument); } 

Des exceptions doivent être utilisées lorsque la situation est attendue mais exceptionnelle (des choses qui échappent au contrôle du consommateur, comme une panne d’E / S). Argument * L’exception est une indication d’un bogue et doit être (mon avis) gérée avec des tests et assistée par Debug.Assert

BTW: Dans ce cas particulier, vous pourriez avoir utilisé le type Month, au lieu de int. C # est insuffisant quand il s’agit de taper la sécurité (Aspect # rulez!) Mais parfois vous pouvez empêcher (ou attraper au moment de la compilation) ces bogues tous ensemble.

Et oui, MicroSoft a tort à ce sujet.

Il existe une exception ArgumentException que vous pouvez utiliser, ou vous pouvez sous-classer et créer les vôtres. Il existe plusieurs classes ArgumentException spécifiques:

http://msdn.microsoft.com/en-us/library/system.argumentexception(VS.71).aspx

Quel que soit celui qui fonctionne le mieux.