Quand est-il approprié de lever une exception depuis un getter ou un setter de propriété? Quand n'est-ce pas approprié? Pourquoi? Des liens vers des documents externes sur le sujet seraient utiles ... Google est étonnamment peu apparu.
c#
.net
exception
properties
Jon Seigel
la source
la source
Réponses:
Microsoft a ses recommandations sur la façon de concevoir des propriétés à l' adresse http://msdn.microsoft.com/en-us/library/ms229006.aspx
Essentiellement, ils recommandent que les getters de propriété soient des accesseurs légers qui peuvent toujours être appelés en toute sécurité. Ils recommandent de redessiner les getters pour qu'ils deviennent des méthodes si des exceptions sont quelque chose que vous devez lancer. Pour les setters, ils indiquent que les exceptions sont une stratégie de gestion des erreurs appropriée et acceptable.
Pour les indexeurs, Microsoft indique qu'il est acceptable pour les getters et les setters de lever des exceptions. Et en fait, de nombreux indexeurs de la bibliothèque .NET le font. L'exception la plus courante étant
ArgumentOutOfRangeException
.Il y a de très bonnes raisons pour lesquelles vous ne voulez pas lever d'exceptions dans les getters de propriété:
obj.PropA.AnotherProp.YetAnother
- avec ce type de syntaxe, il devient problématique de décider où injecter les instructions exception catch.En remarque, il faut savoir que ce n'est pas parce qu'une propriété n'est pas conçue pour lever une exception que cela ne signifie pas qu'elle ne le sera pas; il pourrait facilement appeler le code qui le fait. Même le simple fait d'allouer un nouvel objet (comme une chaîne) peut entraîner des exceptions. Vous devez toujours écrire votre code de manière défensive et vous attendre à des exceptions de tout ce que vous invoquez.
la source
Il n'y a rien de mal à lancer des exceptions de la part des setters. Après tout, quelle meilleure façon d'indiquer que la valeur n'est pas valide pour une propriété donnée?
Pour les getters, il est généralement mal vu, et cela peut être expliqué assez facilement: un getter de propriété, en général, rapporte l'état actuel d'un objet; ainsi, le seul cas où il est raisonnable pour un getter de lancer est celui où l'état est invalide. Mais il est également généralement considéré comme une bonne idée de concevoir vos classes de telle sorte qu'il ne soit tout simplement pas possible d'obtenir un objet invalide au départ, ou de le mettre dans un état invalide par des moyens normaux (c'est-à-dire toujours assurer une initialisation complète dans les constructeurs, et essayez de rendre les méthodes sans exception en ce qui concerne la validité d'état et les invariants de classe). Tant que vous vous en tenez à cette règle, vos getters de propriété ne devraient jamais se retrouver dans une situation où ils doivent signaler un état invalide, et donc ne jamais lancer.
Il y a une exception que je connais, et c'est en fait une exception assez importante: toute implémentation d'objet
IDisposable
.Dispose
est spécifiquement conçu comme un moyen de mettre un objet dans un état invalide, et il existe même une classe d'exception spécialeObjectDisposedException
, à utiliser dans ce cas. Il est parfaitement normal de lancerObjectDisposedException
depuis n'importe quel membre de classe, y compris les récupérateurs de propriété (et s'excluantDispose
lui-même), après que l'objet a été supprimé.la source
IDisposable
devraient être rendus inutiles après unDispose
. Si l'invocation d'un membre nécessiterait l'utilisation d'une ressource quiDispose
a rendu indisponible (par exemple, le membre lirait les données d'un flux qui a été fermé) le membre devrait lancerObjectDisposedException
plutôt que de fuir par exempleArgumentException
, mais si l'on a un formulaire avec des propriétés qui représentent le dans certains champs, il semblerait beaucoup plus utile de permettre la lecture de ces propriétés après l'élimination (donnant les dernières valeurs typées) que d'exiger ...Dispose
seront différés jusqu'à ce que toutes ces propriétés aient été lues. Dans certains cas où un thread peut utiliser des lectures bloquantes sur un objet tandis qu'un autre le ferme, et où des données peuvent arriver à tout moment avantDispose
, il peut être utile d'avoirDispose
coupé les données entrantes, mais autoriser la lecture des données précédemment reçues. Il ne faut pas forcer une distinction artificielle entreClose
etDispose
dans des situations où aucune n'aurait autrement besoin d'exister.Get...
méthode à la place. Une exception ici est lorsque vous devez implémenter une interface existante qui vous oblige à fournir une propriété.Il n'est presque jamais approprié sur un getter, et parfois approprié sur un setter.
La meilleure ressource pour ce genre de questions est "Framework Design Guidelines" par Cwalina et Abrams; il est disponible sous forme de livre relié, et une grande partie de celui-ci est également disponible en ligne.
De la section 5.2: Conception de la propriété
la source
ObjectDisposedException
une fois que l'objet a étéDispose()
appelé et que quelque chose demande par la suite une valeur de propriété? Il semble que les conseils devraient être "éviter de lancer des exceptions à partir des récupérateurs de propriété, à moins que l'objet n'ait été supprimé, auquel cas vous devriez envisager de lancer un ObjectDisposedExcpetion".Une bonne approche des exceptions consiste à les utiliser pour documenter le code pour vous-même et pour d'autres développeurs comme suit:
Les exceptions devraient concerner les états de programme exceptionnels. Cela signifie que vous pouvez les écrire où vous voulez!
Une des raisons pour lesquelles vous voudrez peut-être les mettre dans des getters est de documenter l'API d'une classe - si le logiciel lève une exception dès qu'un programmeur essaie de l'utiliser de manière incorrecte, il ne l'utilisera pas mal! Par exemple, si vous avez une validation pendant un processus de lecture de données, il peut ne pas être logique de pouvoir continuer et d'accéder aux résultats du processus s'il y avait des erreurs fatales dans les données. Dans ce cas, vous souhaiterez peut-être obtenir la sortie de sortie s'il y avait des erreurs pour vous assurer qu'un autre programmeur vérifie cette condition.
Ils sont une manière de documenter les hypothèses et les limites d'un sous-système / méthode / peu importe. Dans le cas général, ils ne doivent pas être pris! C'est aussi parce qu'ils ne sont jamais lancés si le système fonctionne ensemble de la manière attendue: si une exception se produit, cela montre que les hypothèses d'un morceau de code ne sont pas satisfaites - par exemple, il n'interagit pas avec le monde qui l'entoure de la manière c'était initialement prévu. Si vous détectez une exception qui a été écrite à cet effet, cela signifie probablement que le système est entré dans un état imprévisible / incohérent - cela peut finalement conduire à un plantage ou à une corruption de données ou similaire, ce qui sera probablement beaucoup plus difficile à détecter / déboguer.
Les messages d'exception sont un moyen très grossier de signaler les erreurs - ils ne peuvent pas être collectés en masse et ne contiennent vraiment qu'une chaîne. Cela les rend impropres à signaler des problèmes dans les données d'entrée. En fonctionnement normal, le système lui-même ne doit pas entrer dans un état d'erreur. En conséquence, les messages qu'ils contiennent doivent être conçus pour les programmeurs et non pour les utilisateurs - les choses qui ne sont pas correctes dans les données d'entrée peuvent être découvertes et relayées aux utilisateurs dans des formats (personnalisés) plus appropriés.
L'exception (haha!) À cette règle est des choses comme IO où les exceptions ne sont pas sous votre contrôle et ne peuvent pas être vérifiées à l'avance.
la source
Tout cela est documenté dans MSDN (comme lié à d'autres réponses), mais voici une règle générale ...
Dans le setter, si votre propriété doit être validée au-dessus et au-delà du type. Par exemple, une propriété appelée PhoneNumber devrait probablement avoir une validation regex et devrait générer une erreur si le format n'est pas valide.
Pour les getters, peut-être lorsque la valeur est nulle, mais c'est probablement quelque chose que vous voudrez gérer sur le code appelant (selon les directives de conception).
la source
MSDN: intercepter et lancer des types d'exceptions standard
http://msdn.microsoft.com/en-us/library/ms229007.aspx
la source
C'est une question très complexe et la réponse dépend de la façon dont votre objet est utilisé. En règle générale, les getters et les setters de propriété qui sont "à liaison tardive" ne devraient pas lancer d'exceptions, tandis que les propriétés avec exclusivement une "liaison anticipée" devraient lever des exceptions lorsque le besoin s'en fait sentir. BTW, l'outil d'analyse de code de Microsoft définit l'utilisation des propriétés de manière trop étroite à mon avis.
«liaison tardive» signifie que les propriétés sont trouvées par réflexion. Par exemple, l'attribut Serializeable "est utilisé pour sérialiser / désérialiser un objet via ses propriétés. Lancer une exception dans ce genre de situation casse les choses de manière catastrophique et n'est pas un bon moyen d'utiliser des exceptions pour créer un code plus robuste.
«Early binding» signifie qu'une propriété utilisée est liée dans le code par le compilateur. Par exemple, lorsque du code que vous écrivez fait référence à un getter de propriété. Dans ce cas, il est possible de lancer des exceptions lorsqu'elles ont un sens.
Un objet avec des attributs internes a un état déterminé par les valeurs de ces attributs. Les propriétés exprimant des attributs qui sont conscients et sensibles à l'état interne de l'objet ne doivent pas être utilisées pour la liaison tardive. Par exemple, disons que vous avez un objet qui doit être ouvert, accédé, puis fermé. Dans ce cas, accéder aux propriétés sans appeler open en premier devrait entraîner une exception. Supposons, dans ce cas, que nous ne lançions pas d'exception et que nous permettions au code d'accéder à une valeur sans lever d'exception? Le code semblera heureux même s'il a obtenu une valeur d'un getter qui n'a pas de sens. Maintenant, nous avons mis le code qui a appelé le getter dans une mauvaise situation car il doit savoir comment vérifier la valeur pour voir si elle n'a pas de sens. Cela signifie que le code doit faire des hypothèses sur la valeur qu'il a obtenue du getter de propriété afin de le valider. C'est ainsi que le mauvais code est écrit.
la source
J'avais ce code où je ne savais pas quelle exception lancer.
J'ai empêché le modèle d'avoir la propriété null en premier lieu en le forçant comme argument dans le constructeur.
la source