Les propriétés devraient-elles avoir des effets secondaires


19

Les propriétés en C # devraient-elles avoir des effets secondaires en plus de notifier un changement dans ses états?

J'ai vu des propriétés utilisées de différentes manières. Des propriétés qui chargeront la valeur lors de leur premier accès aux propriétés qui ont des effets secondaires massifs comme provoquer une redirection vers une page différente.



1
Parmi les conclusions de @ Job, pourquoi devrais-je éviter d'utiliser les propriétés en C #? L'opinion de Jeffrey Richter est très pertinente pour cette question.
rwong

@rwong Pertinent oui mais complètement insensé - doit néanmoins être lu (afin d'être conscient des problèmes qu'il met en évidence). Au moins sur cette question, je suis au camp de Skeet.
Apoorv Khurasia

Bien que cela concerne les constructeurs, cela est également pertinent: programmers.stackexchange.com/a/164255/1996
Jim G.

Réponses:


40

Je suppose que vous parlez de propriétés en lecture seule , ou du moins de getters de propriétés , car un configurateur de propriétés va, dans presque tous les cas, avoir des effets secondaires. Sinon, ce n'est pas très utile.

En général, un bon design suit le principe de la moindre surprise . Ne faites pas des choses que les appelants ne s'attendent pas à ce que vous fassiez, surtout si ces choses peuvent changer les résultats futurs .

En général , cela signifie que les acquéreurs de propriétés ne devraient pas avoir d'effets secondaires.

Cependant , faisons attention à ce que nous entendons par «effet secondaire».

Un effet secondaire est, techniquement, toute modification d'état. Ce pourrait être un État accessible au public, ou ... ce pourrait être un État totalement privé.

Les chargeurs différés / différés sont un exemple d'état presque exclusivement privé. Tant que ce n'est pas la responsabilité de l'appelant à libérer cette ressource, alors vous êtes en train réduirez la surprise et la complexité en général en utilisant l' initialisation différée. Un appelant ne s'attend pas normalement à devoir signaler explicitement l'initialisation d'une structure interne . Ainsi, l'initialisation paresseuse ne viole pas le principe ci-dessus.

Un autre exemple est une propriété synchronisée. Pour qu'une méthode soit thread-safe, elle devra souvent être protégée par une section critique ou un mutex. L'entrée dans une section critique ou l'acquisition d'un mutex est un effet secondaire; vous modifiez l'état, généralement l' état global . Cependant, cet effet secondaire est nécessaire afin d'éviter une sorte de surprise bien pire - la surprise de la modification des données (ou pire, partiellement modifiée) par un autre thread.

Je voudrais donc assouplir un peu la restriction comme suit: Les lectures de propriétés ne devraient pas avoir d' effets secondaires visibles ou d'effets secondaires qui changent leur sémantique .

S'il n'y a aucun moyen possible pour un appelant d'être affecté ou même conscient d'un effet secondaire, cet effet secondaire ne fait aucun mal. S'il vous est impossible d'écrire un test pour vérifier l'existence d'un effet secondaire particulier, alors il est suffisamment localisé pour être étiqueté comme un détail d'implémentation privé, et donc sans préoccupation légitime pour le monde extérieur.

Mais soyez prudent; en règle générale, vous devriez essayer d'éviter les effets secondaires, parce que souvent ce que vous pensez peut - être un détail de mise en œuvre privée peut fuir de façon inattendue et devenir public.


2
+1 - une belle réponse complète comprenant les cas les plus compliqués qui transforment un "doit" en un "devrait ... sauf quand ...."
rapid_now

Un autre exemple d'un effet secondaire acceptable sur un getter: une fonction de journalisation.
Loren Pechtel

5

Je répondrai à votre question par une question: que se passe-t-il lorsque vous modifiez la propriété Width d'un formulaire?

Juste au sommet de ma tête, cela:

  • Modifiez la valeur du champ de support.
  • Déclenchez un événement.
  • Modifiez les dimensions du formulaire, signalez la modification au gestionnaire de fenêtres et demandez une nouvelle peinture.
  • Avertissez les contrôles sur le formulaire de la modification afin que tous ceux qui doivent changer de taille ou de position lors du redimensionnement du formulaire puissent le faire.
  • Provoquez tous les contrôles qui se sont transformés en événements d'incendie et indiquez au gestionnaire de fenêtres qu'ils doivent être redessinés.
  • Probablement d'autres trucs aussi.

(Avertissement: je suis un développeur Delphi, pas un développeur .NET. Ce n'est peut-être pas précis à 100% pour C #, mais je parie que c'est assez proche, surtout compte tenu de la quantité de framework .NET d'origine basée sur Delphi.)

Tous ces «effets secondaires» se produisent lorsque vous modifiez la propriété Width sur un formulaire. Certains d'entre eux semblent-ils inappropriés ou incorrects?


tant qu'il n'entre pas dans une boucle infinie s'appelant. Pour éviter cela, chaque "modification" sera mappée sur au moins trois événements: un déclenché avant la modification, un déclenché pendant la modification et un déclenché après sa fin.
rwong

5

Jetez un œil aux règles de conception de Microsoft , en particulier l'une d'entre elles:

CA1024: utilisez les propriétés le cas échéant

... Une méthode est un bon candidat pour devenir une propriété si l'une de ces conditions est remplie:

  • Ne prend aucun argument et retourne les informations d'état d'un objet.
  • Accepte un seul argument pour définir une partie de l'état d'un objet.

Les propriétés doivent se comporter comme s'il s'agissait de champs; si la méthode ne peut pas, elle ne doit pas être modifiée en propriété. Les méthodes sont meilleures que les propriétés dans les situations suivantes:

  • Le procédé effectue une opération longue. La méthode est sensiblement plus lente que le temps nécessaire pour définir ou obtenir la valeur d'un champ.
  • La méthode effectue une conversion. L'accès à un champ ne renvoie pas une version convertie des données qu'il stocke.
  • La méthode Get a un effet secondaire observable. La récupération de la valeur d'un champ ne produit aucun effet secondaire.
  • L'ordre d'exécution est important. La définition de la valeur d'un champ ne dépend pas de l'occurrence d'autres opérations.
  • L'appel de la méthode deux fois de suite crée des résultats différents.
  • La méthode est statique mais renvoie un objet qui peut être modifié par l'appelant. La récupération de la valeur d'un champ ne permet pas à l'appelant de modifier les données stockées par le champ.
  • La méthode renvoie un tableau ...

2

Je pense que les propriétés ne devraient pas avoir d'effets secondaires. Il existe cependant une exception à cette règle: le chargement paresseux. Si vous voulez charger quelque chose paresseusement dans une propriété, c'est très bien, car le client ne peut pas dire que le chargement paresseux se déroule et ne s'en soucie généralement pas.

EDIT : Oh, encore une chose - le déclenchement d'un événement disant "PropertyXYZ a changé!" (par exemple INotifyPropertyChanged) - convient aux setters.


2

Outre le chargement paresseux mentionné précédemment, il existe également le cas des fonctions de journalisation. Ce serait rare mais pas hors de question.


2

Il n'y a presque pas d'absolu dans la programmation, afin de répondre à votre question, nous devons examiner certaines propriétés souhaitables des objets et voir si cela s'applique à notre cas.

  1. Nous voulons que les classes soient facilement testables.
  2. Nous voulons que les classes prennent en charge la concurrence
  3. Nous voulons que les cours soient faciles à raisonner pour des tiers

Si nous répondons oui à certaines de ces questions, c'est probablement une bonne idée de réfléchir très attentivement aux propriétés et à leurs effets secondaires. Je suppose que lorsque vous dites des effets secondaires, vous voulez dire des effets secondaires secondaires puisque la mise à jour de la valeur est un effet secondaire en soi.

Plus il y a d'effets secondaires en général, plus une classe sera difficile à tester. Plus les effets secondaires sont secondaires, plus la concurrence sera difficile à gérer, mais c'est un problème difficile qui nécessite probablement une réflexion majeure de toute façon.

Le gros problème est probablement pour les personnes qui traitent votre classe comme une "boîte noire", il ne serait pas facilement disponible qu'un état ait changé juste parce qu'ils ont lu une propriété ou que d'autres propriétés changent parce qu'une autre change (ce qui pourrait conduire aux mises à jour en cascade).

Donc en général non, nous voulons que les propriétés soient aussi faciles à raisonner et aussi simples que possible, c'est un peu impliqué dans la décision de conception, sinon ce serait une méthode. Un bon programmeur peut toujours enfreindre les règles cependant, assurez-vous simplement d'avoir une très bonne raison :)

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.