Quand est-il approprié de lancer une exception à l'intérieur d'un getter ou d'un setter de propriété ? Quand n'est-ce pas approprié ? Pourquoi ? Des liens vers des documents externes sur le sujet seraient utiles... Google n'a pas trouvé grand-chose.
Réponses
Trop de publicités?Microsoft a ses recommandations sur la façon de concevoir les propriétés à http://msdn.microsoft.com/en-us/library/ms229006.aspx
Essentiellement, ils recommandent que les getters de propriétés soient des accesseurs légers dont l'appel est toujours sûr. Ils recommandent de remanier les getters pour en faire des méthodes si les exceptions sont quelque chose qu'il faut lancer. Pour les setters, ils indiquent que les exceptions constituent une stratégie de gestion des erreurs appropriée et acceptable.
Pour les indexeurs, Microsoft indique qu'il est acceptable que les getters et les setters lancent des exceptions. C'est d'ailleurs ce que font de nombreux indexeurs de la bibliothèque .NET. L'exception la plus courante étant ArgumentOutOfRangeException
.
Il y a de bonnes raisons de ne pas lancer d'exceptions dans les getters de propriétés :
- Étant donné que les propriétés "semblent" être des champs, il n'est pas toujours évident qu'elles puissent lancer une exception (par conception), alors qu'avec les méthodes, les programmeurs sont formés pour s'attendre à ce que les exceptions soient une conséquence attendue de l'invocation de la méthode et pour chercher à savoir si c'est le cas.
- Les Getters sont utilisés par de nombreuses infrastructures .NET, comme les sérialiseurs et les liaisons de données (dans WinForms et WPF par exemple) - la gestion des exceptions dans de tels contextes peut rapidement devenir problématique.
- Les Property Getters sont automatiquement évalués par les débogueurs lorsque vous observez ou inspectez un objet. Une exception à ce niveau peut être source de confusion et ralentir vos efforts de débogage. Pour les mêmes raisons, il n'est pas souhaitable d'effectuer d'autres opérations coûteuses dans les propriétés (comme l'accès à une base de données).
- Les propriétés sont souvent utilisées dans une convention de chaînage :
obj.PropA.AnotherProp.YetAnother
- Avec ce type de syntaxe, il devient problématique de décider où injecter les instructions de capture d'exception.
Par ailleurs, il faut savoir que ce n'est pas parce qu'un bien est non conçu de lancer une exception, cela ne signifie pas qu'il ne le fera pas ; il pourrait facilement appeler un code qui le fait. Même le simple fait d'allouer un nouvel objet (comme une chaîne de caractères) peut entraîner des exceptions. Vous devez toujours écrire votre code de manière défensive et vous attendre à des exceptions de la part de tout ce que vous invoquez.
Il n'y a rien de répréhensible à lancer des exceptions à partir d'un "setters". Après tout, quel meilleur moyen d'indiquer que la valeur n'est pas valide pour une propriété donnée ?
Pour les getters, c'est généralement mal vu, et cela s'explique 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 jeter est lorsque l'état est invalide. Mais il est aussi 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 qu'il faut toujours assurer une initialisation complète dans les constructeurs, et essayer de rendre les méthodes à l'abri des exceptions en ce qui concerne la validité de l'état et les invariants de classe). Tant que vous respectez cette règle, vos Property Getters 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 elle est en fait assez importante : tout objet qui implémente IDisposable
. Dispose
est spécifiquement conçu comme un moyen d'amener un objet dans un état invalide, et il existe même une classe d'exception spéciale, ObjectDisposedException
à utiliser dans ce cas. Il est tout à fait normal de jeter ObjectDisposedException
de n'importe quel membre de la classe, y compris les getters de propriété (et à l'exclusion des Dispose
lui-même), après que l'objet a été éliminé.
Il n'est presque jamais approprié sur un getter, et parfois sur un setter.
La meilleure ressource pour ce type de questions est "Framework Design Guidelines" de Cwalina et Abrams ; il s'agit d'un livre relié, dont une grande partie est également disponible en ligne.
Extrait de la section 5.2 : Conception des biens
ÉVITER de lancer des exceptions à partir de de propriété. Les getters de propriété doivent être des opérations simples et ne doivent pas ne doivent pas avoir de conditions préalables. Si un getter peut générer une exception, il doit probablement être transformé en méthode. Notez que cette règle ne s'applique pas aux où nous nous attendons à des des exceptions suite à la validation des les arguments.
Il convient de noter que cette ligne directrice ne s'applique qu'aux qu'aux getters de propriétés. Il est acceptable de lancer une exception dans une propriété setter.
Tout cela est documenté dans MSDN (comme indiqué dans d'autres réponses), mais voici une règle générale...
Dans le setter, si votre propriété doit être validée au-delà du type. Par exemple, une propriété appelée PhoneNumber devrait probablement être validée par des expressions rationnelles et générer une erreur si le format n'est pas valide.
Pour les getters, éventuellement lorsque la valeur est nulle, mais c'est très probablement quelque chose que vous voudrez gérer dans le code appelant (conformément aux directives de conception).