Le lancement d'une exception à partir d'une propriété est-il mauvais?


15

J'ai toujours été d'avis que les propriétés (c'est-à-dire leurs opérations set / get) devraient être rapides / immédiates et sans échec. Vous ne devriez jamais avoir à essayer / attraper pour obtenir ou définir une propriété.

Mais j'examine quelques façons d'appliquer la sécurité basée sur les rôles aux propriétés de certains objets. Par exemple, une propriété Employee.Salary. Certaines des solutions que j'ai rencontrées que d'autres ont essayées (une en particulier est l'exemple AOP ici ) impliquent de lever une exception si l'accesseur n'a pas les bonnes autorisations - mais cela va à l'encontre d'une règle personnelle que j'ai eue depuis longtemps maintenant.

Alors je demande: ai-je tort? Les choses ont-elles changé? Est-il admis que les propriétés devraient pouvoir lever des exceptions?


1
Cette même question sur StackOverflow a attiré plus d'attention et donc de meilleures réponses.
Roman Starkov

Réponses:


14

lorsque vous définissez la valeur d'une propriété, lever une exception sur une valeur non valide est très bien

obtenir la valeur d'une propriété ne devrait (presque) jamais lever d'exception

pour un accès basé sur les rôles, utilisez des interfaces ou des façades différentes / plus stupides; ne laissez pas les gens voir des choses qu'ils ne peuvent pas avoir!


5

Je considère que c'est surtout une mauvaise forme, mais même Microsoft recommande parfois d'utiliser des exceptions. Un bon exemple est la propriété abstraite Stream.Length . Selon les directives, je serais plus soucieux d'éviter les effets secondaires sur les getters et de limiter les effets secondaires sur les setters.


4

Je dirais certainement qu'il y a une faille dans la conception si vous ressentez le besoin de lever des exceptions à partir d'un setter de propriété ou d'un getter.

Une propriété est une abstraction qui représente quelque chose qui n'est qu'une valeur . Et vous devriez pouvoir définir une valeur sans craindre que cela ne lève une exception. *

Si la définition de la propriété entraîne un effet secondaire, cela devrait vraiment être implémenté en tant que méthode. Et si cela ne produit aucun effet secondaire, aucune exception ne doit être levée.

Un exemple déjà mentionné dans une réponse différente est la Stream.Positionpropriété. Cela produit des effets secondaires et peut lever des exceptions. Mais cet ensemble de propriétés n'est en fait qu'un wrapper Stream.Seekque vous pourriez appeler à la place.

Personnellement, je crois que le poste n'aurait pas dû être une propriété accessible en écriture.

Un autre exemple où vous pourriez être tenté de lever une exception à partir d'un ensemble de propriétés est dans la validation des données:

public class User {
    public string Email {
        get { return _email; }
        set { 
            if (!IsValidEmail(value)) throw InvalidEmailException(value);
            _email = value;
        }
    }

Mais il existe une meilleure solution à ce problème. Introduisez un type représentant une adresse e-mail valide:

public class Email {
    public Email(string value) {
        if (!IsValidEmail(value)) throw new InvalidEmailException(value);
        ...
    }
    ...
}

public class User {
    public Email Email { get; set; }
}

La Emailclasse s'assure qu'elle ne peut pas contenir une valeur qui n'est pas une adresse e-mail valide, et les classes qui ont besoin de stocker des e-mails sont déchargées de l'obligation de les valider.

Cela conduit également à une cohésion plus élevée (un indicateur d'une bonne conception logicielle) - la connaissance de ce qu'est une adresse e-mail et de la façon dont elle est validée n'existe que dans la Emailclasse, qui n'a que cette préoccupation.

* ObjectDisposedException est la seule exception valide (sans jeu de mots) à laquelle je peux penser en ce moment.


2

Je sais que votre question est spécifique à .NET , mais comme C # partage un peu d'histoire avec Java, j'ai pensé que cela pourrait vous intéresser. Je suis pas de quelque façon que ce qui implique que parce que quelque chose se fait en Java, il doit être fait en C #. Je sais que les deux sont très différents, en particulier dans la façon dont C # prend en charge les propriétés au niveau du langage. Je donne juste un peu de contexte et de perspective.

De la spécification JavaBeans :

Propriétés contraintes Parfois, lorsqu'un changement de propriété se produit, un autre bean peut souhaiter valider le changement et le rejeter s'il est inapproprié. Nous faisons référence aux propriétés soumises à ce type de vérification en tant que propriétés contraintes. Dans Java Beans, des méthodes de définition de propriété contraintes sont requises pour prendre en charge la propriété PropertyVetoException. Cela indique aux utilisateurs de la propriété contrainte que les tentatives de mise à jour peuvent être refusées. Ainsi, une simple propriété contrainte pourrait ressembler à:

PropertyType getFoo();
void setFoo(PropertyType value) throws PropertyVetoException;

Prenez tout cela avec un grain de sel, s'il vous plaît. La spécification JavaBeans est ancienne et les propriétés C # sont (IMO) une énorme amélioration par rapport aux propriétés basées sur la "convention de dénomination" de Java. J'essaie juste de donner un peu de contexte, c'est tout!


C'est un bon point, mais la distinction entre une méthode de définition et la définition de propriété ac # est suffisamment significative pour être un cas différent pour moi. De même, les exceptions vérifiées de Java forcent le code appelant à savoir qu'une exception peut être levée, il y a donc moins de chance que cela surprenne quelqu'un.
Steven Evers

2

Le point d'une propriété est le principe d'accès uniforme , c'est-à-dire qu'une valeur doit être accessible via la même interface, qu'elle soit implémentée par stockage ou par calcul. Si votre propriété lève une exception qui représente une condition d'erreur indépendante de la volonté du programmeur, du type qui est censé être intercepté et géré, vous obligez votre client à savoir que la valeur est obtenue via le calcul.

D'un autre côté, je ne vois aucun problème à utiliser des assertions ou des exceptions de type assertion qui sont destinées à signaler une utilisation incorrecte de l'API au programmeur plutôt que d'être interceptées et gérées. Dans ces cas, la bonne réponse du point de vue de l'utilisateur de l'API n'est pas de gérer l'exception (se souciant ainsi implicitement de savoir si la valeur est obtenue via le calcul ou le stockage). C'est pour corriger son code afin que l'objet ne se retrouve pas dans un état invalide ou pour vous faire corriger votre code afin que l'assertion ne se déclenche pas.


Je ne vois pas comment «vous forcez votre client à savoir que la valeur est obtenue via le calcul» suit. L'accès au stockage peut certainement aussi générer une exception. En fait, comment définiriez-vous même la différence entre les deux?
Timwi

Je pense que la distinction qui est faite est que le simple fait de récupérer une valeur dans un champ (stockage) et de faire quelque chose d'inoffensif avec, comme le placer dans une variable d'un type approprié (c'est-à-dire sans transtypage), ne peut pas lever d'exception. Dans ce cas, une exception ne peut se produire que si vous avez ensuite fait quelque chose avec la valeur. Si une exception est levée dans un getter de propriété, alors dans ce cas, le simple fait de récupérer la valeur et de la placer dans une variable peut provoquer une exception.
Dr.Wily's Apprentice

1

AFAIK, ces conseils proviennent principalement du processus de réflexion que les propriétés peuvent finir par être utilisées au moment de la conception . (Par exemple, la propriété Text sur un TextBox) Si la propriété lève une exception lorsqu'un concepteur essaie d'y accéder, VS ne va pas passer une bonne journée. Vous obtiendrez un tas d'erreurs juste en essayant d'utiliser le concepteur et pour les concepteurs d'interface utilisateur, ils ne seront pas rendus. Cela s'applique également au temps de débogage, bien que ce que vous verrez dans un scénario d'exception ne soit que "xxx Exception" et qu'il n'encombre pas VS IIRC.

Pour les POCO, cela ne fera pas vraiment de mal, mais je répugne toujours à le faire moi-même. Je pense que les gens voudront accéder aux propriétés plus fréquemment, donc ils devraient normalement être à faible coût. Les propriétés ne devraient pas faire le travail des méthodes, elles devraient juste obtenir / définir des informations et en faire en règle générale.


0

Bien que beaucoup dépende du contexte, j'éviterais généralement de mettre toute sorte de logique cassable (y compris les exceptions) dans les propriétés. À titre d'exemple, il y a beaucoup de choses que vous (ou une bibliothèque que vous utilisez) pourriez faire qui utilisent la réflexion pour parcourir les propriétés, entraînant des erreurs que vous n'aviez pas prévues au moment de la conception.

Je dis généralement, car il peut y avoir des moments où vous voulez absolument, définitivement, bloquer quelque chose sans trop vous soucier des conséquences. La sécurité, je suppose, serait le cas classique là-bas.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.