La garantie de l'immuabilité est-elle une justification pour exposer un champ au lieu d'une propriété?


10

L'orientation générale pour C # est de toujours utiliser une propriété sur un domaine public. Cela a du sens: en exposant un champ, vous exposez beaucoup de détails d'implémentation. Avec une propriété, vous encapsulez ce détail afin qu'il soit caché de la consommation de code, et les changements d'implémentation sont découplés des changements d'interface.

Cependant, je me demande s'il existe parfois une exception valide à cette règle lors de l'utilisation du readonlymot clé. En appliquant ce mot-clé à un domaine public, vous faites une garantie supplémentaire: l'immuabilité. Ce n'est pas seulement un détail d'implémentation, l'immuabilité est quelque chose qui pourrait intéresser un consommateur. L'utilisation d'un readonlychamp le fait partie du contrat public, et quelque chose qui ne peut pas être rompu par de futurs changements ou héritages sans avoir à modifier l'interface publique. C'est quelque chose qu'une propriété ne peut pas offrir.

La garantie de l'immuabilité est-elle donc une raison légitime de choisir un readonlychamp plutôt qu'une propriété dans certains cas?

(Pour plus de précision, je ne dis certainement pas que vous devriez toujours faire ce choix juste parce que le champ est immuable en ce moment, uniquement lorsqu'il a du sens dans le cadre de la conception de la classe et que son utilisation prévue inclut l'immuabilité dans son contrat. Je suis surtout intéressé par des réponses se concentrant sur la question de savoir si cela peut être justifié, plutôt que sur des cas spécifiques où ce n'est pas le cas, par exemple lorsque vous avez besoin que le membre soit sur un interfaceou que vous souhaitiez effectuer un chargement paresseux.)



1
@gnat Juste pour clarifier, par "ReadOnly Property", ils utilisent la terminologie VB.NET qui signifie une propriété sans setter, pas un champ en lecture seule. Pour les besoins de ma question, les deux options que cette question compare sont équivalentes - elles utilisent toutes les deux des propriétés.
Ben Aaronson

Réponses:


6

Les champs publics statiques en lecture seule sont certainement corrects. Ils sont recommandés dans certaines situations, comme lorsque vous voulez un objet constant nommé mais que vous ne pouvez pas utiliser le constmot - clé.

Les champs membres en lecture seule sont un peu plus compliqués. Il n'y a pas beaucoup d'avantages sur une propriété sans setter public, et il y a un inconvénient majeur: les champs ne peuvent pas être membres d'interfaces. Donc, si vous décidez de refactoriser votre code pour qu'il fonctionne contre une interface au lieu d'un type concret, vous devrez changer vos champs en lecture seule en propriétés avec des getters publics.

Une propriété publique non virtuelle sans setter protégé offre une protection contre l'héritage, sauf si les classes héritées ont accès au champ de sauvegarde. Vous pouvez appliquer complètement ce contrat dans la classe de base.

Ne pas pouvoir changer "l'immuabilité" du membre sans changer l'interface publique de la classe n'est pas vraiment un obstacle dans ce cas. Habituellement, si vous souhaitez transformer un champ public en propriété, cela se passe bien, sauf qu'il y a deux cas à traiter:

  1. Tout ce qui écrit dans un membre de cet objet, comme: object.Location.X = 4n'est possible que s'il Locations'agit d'un champ. Cela n'est cependant pas pertinent pour un champ en lecture seule, car vous ne pouvez pas modifier une structure en lecture seule. (Cela suppose que Locationc'est un type de valeur - sinon, ce problème ne s'appliquerait pas de toute façon car readonlyil ne protège pas contre ce genre de chose.)
  2. Tout appel à une méthode qui transmet la valeur sous la forme outou ref. Encore une fois, cela n'est pas vraiment pertinent pour un champ en lecture seule, car les paramètres outet refont tendance à être modifiés, et c'est une erreur de compilation de toute façon de passer une valeur en lecture seule comme out ou ref.

En d'autres termes, bien que la conversion d'un champ en lecture seule en une propriété en lecture seule rompt la compatibilité binaire, d'autres codes source n'auraient qu'à être recompilés sans aucune modification pour gérer cette modification. Cela rend la garantie vraiment facile à briser.

Je ne vois aucun avantage dans le readonlychamp membre, et l'incapacité d'avoir ce genre de chose sur une interface est assez pour moi un inconvénient que je ne l'utiliserais pas.


Par intérêt, pourquoi sont statiques publics champs readonly ok? Est-ce simplement parce que l'exclusion des méthodes d'instance supprime la plupart des cas d'utilisation des propriétés sur les champs immuables (comme le comptage des hits, le chargement différé, etc.)?
Ben Aaronson

Le principal inconvénient d'un champ public en lecture seule par rapport à une propriété en lecture seule a disparu - les membres statiques ne peuvent pas faire partie des interfaces, ils sont donc sur un pied d'égalité. Une propriété statique en lecture seule a besoin d'un stockage initialisé ou doit reconstruire sa valeur de retour chaque fois qu'elle est appelée, tandis qu'un champ en lecture seule aura une syntaxe plus simple et sera initialisé par le constructeur statique. Je pense donc qu'un champ public en lecture seule statique a le potentiel pour une plus grande optimisation par le JIT avec aucun des inconvénients que vous auriez normalement d'exposer un champ.
Erik

2

D'après mon expérience, le readonlymot-clé n'est pas la magie qu'il promet.

Par exemple, vous avez une classe où le constructeur était simple. Compte tenu de l'utilisation (et du fait que certaines propriétés / champs de classe sont immuables après la construction), vous pourriez penser que cela n'a pas d'importance, vous avez donc utilisé le readonlymot - clé.

Plus tard, la classe devient plus complexe, tout comme son constructeur. (Supposons que cela se produise dans un projet expérimental ou dont la vitesse de zoom est suffisamment élevée hors de la portée / du contrôle, mais vous devez le faire fonctionner d'une manière ou d'une autre.) Vous constaterez que les champs qui ne readonlypeuvent être modifiés qu'à l'intérieur du constructeur - vous ne peut pas le modifier dans les méthodes même si cette méthode n'est appelée qu'à partir d'un constructeur.

Cette limitation technique a été reconnue par les concepteurs de C # et pourrait être corrigée dans une future version. Mais jusqu'à ce qu'il soit corrigé, l'utilisation de readonlyest plus restrictive et pourrait encourager un mauvais style de codage (tout mettre dans la méthode constructeur).

Pour rappel, ni la readonlypropriété ni le non-public-setter ne garantissent un "objet entièrement initialisé". En d'autres termes, c'est le concepteur d'une classe qui décide ce que signifie "complètement initialisé" et comment le protéger contre une utilisation par inadvertance, et une telle protection n'est généralement pas infaillible, ce qui signifie que quelqu'un pourrait trouver un moyen de contourner ce problème.


1
Je préfère garder les constructeurs simples - voir brendan.enrick.com/post/… , blog.ploeh.dk/2011/03/03/InjectionConstructorsshouldbesimple donc en fait, encourage en lecture seule un bon style de codage
AlexFoxGill

1
Un constructeur peut passer un readonlychamp en tant que paramètre outou refà d'autres méthodes, qui seront ensuite libres de le modifier comme bon leur semble.
supercat

1

Les champs sont TOUJOURS des détails d'implémentation. Vous ne voulez pas exposer les détails de votre implémentation.

Un principe fondamental du génie logiciel est que nous ne savons pas quelles seront les exigences à l'avenir. Bien que votre readonlydomaine soit bon aujourd'hui, rien ne permet de penser qu'il fera partie des exigences de demain. Que se passe-t-il si demain il est nécessaire d'implémenter un compteur d'accès pour déterminer le nombre d'accès à ce champ? Que faire si vous devez autoriser des sous-classes à remplacer le champ?

Les propriétés sont si faciles à utiliser et sont tellement plus flexibles que les champs publics que je ne peux pas penser à un cas d'utilisation unique où je choisirais à la fois c # comme langue et utiliserais des champs publics en lecture seule sur une classe. Si les performances étaient si serrées que je ne pouvais pas supporter l'appel de méthode supplémentaire, j'utiliserais probablement un langage différent.

Ce n'est pas parce que nous pouvons faire quelque chose avec la langue que nous devons faire quelque chose avec la langue.

En fait, je me demande si les concepteurs de langages regrettent que les champs soient rendus publics. Je ne me souviens pas de la dernière fois où j'ai même envisagé d'utiliser des champs publics non const dans mon code.


1
Je comprends l'importance de construire dans la flexibilité future, mais sûrement si j'écris une classe comme, disons, Rationalavec public, immuable Numeratoret Denominatormembres, je pourrais être extrêmement confiant que ces membres n'auront pas besoin de chargement paresseux ou d'un compteur de hit ou similaire?
Ben Aaronson

Mais pouvez-vous être sûr que l'implémentation utilisant des floattypes pour le Numeratoret Denominatorn'aura pas besoin d'être changée en doubles?
Stephen

1
Eh bien, euh, non, ils ne devraient certainement pas être ni floatni double. Ils devraient l'être int, longou BigInteger. Peut-être que ceux-ci doivent changer ... mais si c'est le cas, ils devraient également changer dans l'interface publique, donc vraiment l'utilisation des propriétés n'ajoute aucune encapsulation autour de ce détail.
Ben Aaronson

-3

Non, ce n'est pas une justification.

Utiliser le readonly comme garantie d'inmutabilité et non comme une justification ressemble plus à un hack.

En lecture seule signifie que la valeur est affectée par un constructeur o lorsque la variable est définie.

Je pense que ce sera une mauvaise pratique

Si vous voulez vraiment garantir l'immuabilité, utilisez une interface qui a plus d'avantages que d'inconvénients.

Exemple d'interface simple: entrez la description de l'image ici


1
"utiliser une interface" Comment?
Ben Aaronson
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.