Meilleures pratiques: lancer des exceptions à partir de propriétés

Quand est-il approprié de lancer une exception à partir d'un getter ou d'un setter de propriété? Quand est-il pas approprié? Pourquoi? Des liens vers des documents externes sur le sujet seraient utiles... Google est apparu étonnamment peu.

96
demandé sur Jon Seigel 2009-09-28 22:01:47

8 réponses

Microsoft a ses recommandations sur la façon de concevoir des propriétés à http://msdn.microsoft.com/en-us/library/ms229006.aspx

Essentiellement, ils recommandent que les getters de propriété soient des accesseurs légers qui sont toujours sûrs à appeler. Ils recommandent de redessiner les getters pour qu'ils soient des méthodes si les 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 que il est acceptable pour les getters et les setters de lancer des exceptions. Et en fait, de nombreux indexeurs dans 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 lancer d'exceptions dans les getters de propriétés:

  • parce que les propriétés "semblent" être des champs, il n'est pas toujours évident qu'elles peuvent lancer une exception (par conception); alors qu'avec les méthodes, les programmeurs sont formés pour attendre et étudier si les exceptions sont un conséquence attendue de l'appel de la méthode.
  • les Getters sont utilisés par beaucoup d'infrastructure.net, comme les sérialiseurs et la liaison de données (dans WinForms et WPF par exemple) - traiter les exceptions dans de tels contextes peut rapidement devenir problématique.
  • les getters de propriétés sont automatiquement évalués par les débogueurs lorsque vous regardez ou inspectez un objet. Une exception ici peut être déroutante et ralentir vos efforts de débogage. Il est également indésirable d'effectuer d'autres opérations coûteuses dans propriétés (comme l'accès à une base de données) pour les mêmes raisons.
  • 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 des instructions de capture d'exception.

Comme note de côté, il faut savoir que ce n'est pas parce qu'une propriété est pas conçue pour lancer une exception, cela ne signifie pas que ce ne sera pas le cas; il pourrait facilement appeler du code qui le fait. Même le simple fait d'allouer un nouvel objet (comme un string) pourrait entraîner des exceptions. Vous devez toujours écrire votre code de manière défensive et attendre des exceptions de tout ce que vous invoquez.

116
répondu LBushkin 2015-02-09 13:25:34

Il n'y a rien de mal à lancer des exceptions de 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, signale l'état actuel d'un objet; ainsi, le seul cas où il est raisonnable pour un getter de lancer est lorsque 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 n'est tout simplement pas possible d'obtenir un objet invalide initialement, ou de le mettre dans un état invalide via des moyens normaux (c'est-à-dire, assurez-vous toujours une initialisation complète dans les constructeurs, et essayez de rendre les méthodes sûres en ce qui concerne la validité de l'état et les invariants de classe). Tant que vous respectez cette règle, vos acquéreurs de propriété ne devraient jamais entrer dans une situation où ils doivent signaler un état invalide, et donc ne jamais jeter.

Il y a une exception que je connais, et c'est en fait un plutôt majeur: tout objet implémentant IDisposable. Dispose est spécifiquement conçu comme un moyen d'amener l'objet dans un état invalide, et il y a même une classe d'exception spéciale, ObjectDisposedException, à utiliser dans ce cas. Il est parfaitement normal de lancer ObjectDisposedException à partir de n'importe quel membre de la classe, y compris les getters de propriété (et à l'exclusion de Dispose lui-même), après que l'objet a été éliminé.

33
répondu Pavel Minaev 2009-09-28 18:23:58

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é

Évitez de lancer des exceptions de bien que les accesseurs. Bien que les accesseurs devrait être des opérations simples et devrait pas de conditions préalables. Si un getter peut lancer une exception, il devrait probablement être redessiné pour être une méthode. Notez que cette règle ne s'applique pas à indexeurs, où nous attendons exceptions résultant de la validation argument.

Notez que cette directive s'applique uniquement aux getters de propriété. Il est OK pour jeter une exception dans un setter de propriété.

23
répondu Eric Lippert 2009-09-28 18:06:41

Une bonne approche des Exceptions consiste à les utiliser pour documenter le code pour vous-même et d'autres développeurs comme suit:

Les Exceptions devraient être pour les états de programme exceptionnels. Cela signifie qu'il est bon de les écrire où vous voulez!

Une raison pour laquelle vous pourriez vouloir les mettre dans 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 mal, alors ils ne l'utiliseront pas mal! Par exemple si vous avez une validation pendant une donnée processus de lecture il peut ne pas être logique de pouvoir continuer et accéder aux résultats du processus s'il y avait des erreurs fatales dans les données. Dans ce cas, vous voudrez peut-être obtenir le lancer de sortie s'il y avait des erreurs pour vous assurer qu'un autre programmeur vérifie cette condition.

Ils sont un moyen de documenter les hypothèses et les limites d'un sous-système/méthode/quoi que ce soit. Dans le cas général, ils ne devraient pas être pris! C'est aussi parce qu'ils ne sont jamais jetés si le système est travailler 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 remplies - par exemple, il n'interagit pas avec le monde qui l'entoure comme il était initialement prévu. Si vous attrapez une exception qui a été écrite à cette fin, 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 qui est susceptible d'être beaucoup plus difficile à détecter/déboguer.

Exception les messages 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 inadaptés pour 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 devraient être conçus pour les programmeurs et non pour les utilisateurs - les choses qui ne vont pas 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!) pour cette règle, il y a des choses comme IO où les exceptions ne sont pas sous votre contrôle et ne peuvent pas être vérifiées à l'avance.

2
répondu Jonny Leeds 2012-07-13 16:26:54

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-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 null, mais très probablement c'est quelque chose que vous voudrez gérer sur le code appelant (selon la conception directive).

1
répondu David Stratton 2009-09-28 18:13:31

MSDN: attraper et lancer des types D'Exception Standard

Http://msdn.microsoft.com/en-us/library/ms229007.aspx

0
répondu smack0007 2009-09-28 18:04:39

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 de propriété et les setters qui sont " liaison tardive "ne devraient pas lancer d'exceptions, tandis que les propriétés avec exclusivement" liaison précoce " devraient lancer 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 trop étroitement à mon avis.

"liaison tardive" signifie que les propriétés sont trouvées par réflexion. Par exemple le Serializeable" attribut 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 rendre le code plus robuste.

"liaison anticipée" signifie qu'une propriété est lié dans le code par le compilateur. Par exemple, quand un code que vous écrivez fait référence à un getter de propriété. Dans ce cas, il est correct de lancer des exceptions quand elles ont du sens.

Un objet avec interne attributs 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, accessible, puis fermé. Dans ce cas, l'accès aux propriétés sans appeler open first devrait entraîner une exception. Supposons, dans ce cas, que nous ne lançons pas d'exception et que nous autorisons le code à accéder à une valeur sans lancer une exception? Le code semblera heureux même s'il a obtenu une valeur d'un getter qui est non-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 est non-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.

0
répondu Jack D Menendez 2013-07-18 00:33:49

J'avais ce code où je ne savais pas quelle exception lancer.

public Person
{
    public string Name { get; set; }
    public boolean HasPets { get; set; }
}

public void Foo(Person person)
{
    if (person.Name == null) {
        throw new Exception("Name of person is null.");
        // I was unsure of which exception to throw here.
    }

    Console.WriteLine("Name is: " + person.Name);
}

J'ai empêché le modèle d'avoir la propriété nulle en premier lieu en le forçant comme argument dans le constructeur.

public Person
{
    public Person(string name)
    {
        if (name == null) {
            throw new ArgumentNullException(nameof(name));
        }
        Name = name;
    }

    public string Name { get; private set; }
    public boolean HasPets { get; set; }
}

public void Foo(Person person)
{
    Console.WriteLine("Name is: " + person.Name);
}
0
répondu Fred 2016-04-11 09:17:10