156 votes

Pourquoi les champs privés sont-ils privés pour le type et non pour l'instance ?

En C# (et dans de nombreux autres langages), il est parfaitement légitime d'accéder aux champs privés d'autres instances du même type. Par exemple :

public class Foo
{
    private bool aBool;

    public void DoBar(Foo anotherFoo)
    {
        if (anotherFoo.aBool) ...
    }
}

Comme le Spécification C# (sections 3.5.1, 3.5.2) stipule que l'accès aux champs privés se fait sur un type, et non sur une instance. J'en ai discuté avec un collègue et nous essayons de trouver une raison pour laquelle cela fonctionne ainsi (plutôt que de restreindre l'accès à la même instance).

Le meilleur argument que nous ayons pu trouver concerne les contrôles d'égalité où la classe peut vouloir accéder à des champs privés pour déterminer l'égalité avec une autre instance. Y a-t-il d'autres raisons ? Ou une raison en or qui signifie absolument que ça doit fonctionner comme ça ou que quelque chose serait complètement impossible ?

18 votes

Quels sont les avantages de l'alternative ?

19 votes

Il est vrai qu'il ne modélise pas très bien le monde réel. Après tout, ce n'est pas parce que ma voiture peut indiquer combien d'essence il lui reste que ma voiture devrait être capable de dire combien d'essence il reste à TOUTES les voitures. Donc, oui, je peux théoriquement envisager d'avoir un accès "instance privée". J'ai l'impression que je ne l'utiliserais jamais parce que je craindrais que dans 99% des cas, je voudrais vraiment qu'une autre instance accède à cette variable.

2 votes

La position qui a été adoptée pour les soi-disant "avantages" est que les classes ne devraient pas connaître l'état interne d'une autre instance - indépendamment du fait qu'il s'agit du même type. Ce n'est pas un avantage en soi, mais il y a probablement une bonne raison de choisir l'une plutôt que l'autre et c'est ce que nous avons essayé de déterminer.

75voto

dlev Points 28160

Je pense qu'une des raisons pour lesquelles ça marche comme ça, c'est parce que les modificateurs d'accès fonctionnent à temps de compilation . En tant que tel, déterminer si un objet donné est ou non le actuel n'est pas facile à faire. Par exemple, considérez ce code :

public class Foo
{
    private int bar;

    public void Baz(Foo other)
    {
        other.bar = 2;
    }

    public void Boo()
    {
        Baz(this);
    }
}

Le compilateur peut-il nécessairement comprendre que other est en fait this ? Pas dans tous les cas. On pourrait argumenter que cela ne devrait pas compiler, mais cela signifie que nous avons un chemin de code où un membre d'instance privé de l'instance correcte n'est pas accessible, ce qui, à mon avis, est encore pire.

Le fait de n'exiger qu'une visibilité au niveau du type plutôt qu'au niveau de l'objet permet de garantir que le problème est traitable, et de faire en sorte qu'une situation qui semble être devrait travail en fait travail.

EDITAR : L'argument de Danilel Hilgarth selon lequel ce raisonnement est à l'envers a du mérite. Les concepteurs de langage peuvent créer le langage qu'ils veulent, et les auteurs de compilateurs doivent s'y conformer. Ceci étant dit, les concepteurs de langage ont un certain intérêt à faciliter le travail des auteurs de compilateurs. (Bien que dans ce cas, il est assez facile d'argumenter que les membres privés pourraient alors uniquement sont accessibles via this (de manière implicite ou explicite)).

Cependant, je pense que cela rend la question plus confuse qu'elle ne doit l'être. La plupart des utilisateurs (moi y compris) trouveraient inutilement contraignant que le code ci-dessus ne fonctionne pas : après tout, c'est mon données auxquelles j'essaie d'accéder ! Pourquoi devrais-je passer par this ?

En bref, je pense que j'ai peut-être exagéré le fait que ce soit "difficile" pour le compilateur. Ce que je voulais vraiment dire, c'est que la situation ci-dessus semble être celle que les concepteurs aimeraient voir fonctionner.

33 votes

À mon avis, ce raisonnement est à l'envers. Le compilateur applique les règles du langage. Il ne les FAIT pas. En d'autres termes, si les membres privés étaient "instance private" et non "type private", le compilateur aurait été implémenté d'une autre manière, par exemple en autorisant seulement this.bar = 2 mais pas other.bar = 2 parce que other pourrait être une instance différente.

0 votes

@Daniel C'est un bon point, mais comme je l'ai mentionné, je pense qu'avoir cette restriction serait pire que d'avoir une visibilité au niveau de la classe.

3 votes

@dlev : Je n'ai rien dit sur le fait que je pense ou non que les membres de l'"instance privée" sont une bonne chose :-) Je voulais juste souligner, que vous argumentez qu'une implémentation technique dicte les règles du langage et qu'à mon avis, cette argumentation n'est pas correcte.

61voto

Goran Jovic Points 5882

Parce que le but de le type d'encapsulation utilisé en C# et dans des langages similaires * vise à réduire la dépendance mutuelle de différents morceaux de code (classes en C# et Java), et non de différents objets en mémoire.

Par exemple, si vous écrivez du code dans une classe qui utilise certains champs dans une autre classe, alors ces classes sont très étroitement couplées. Cependant, si vous avez affaire à un code dans lequel vous avez deux objets de la même classe, alors il n'y a pas de dépendance supplémentaire. Une classe dépend toujours d'elle-même.

Cependant, toute cette théorie sur l'encapsulation échoue dès que quelqu'un crée des propriétés (ou des paires get/set en Java) et expose tous les champs directement, ce qui rend les classes aussi couplées que si elles accédaient aux champs de toute façon.

*Pour des précisions sur les types d'encapsulation, voir l'excellente réponse d'Abel.

3 votes

Je ne pense pas qu'il échoue une seule fois get/set des méthodes sont fournies. Les méthodes d'accès maintiennent toujours une abstraction (l'utilisateur n'a pas besoin de savoir qu'il y a un champ derrière, ou quel(s) champ(s) peut se trouver derrière la propriété). Par exemple, si nous écrivons un Complex nous pouvons exposer des propriétés pour obtenir/régler les coordonnées polaires, et une autre pour obtenir/régler les coordonnées cartésiennes. L'utilisateur ne sait pas laquelle de ces propriétés la classe utilise en dessous (il pourrait même s'agir de quelque chose de complètement différent).

5 votes

@Ken : Cela échoue si vous fournissez une propriété pour chaque champ que vous avez sans même considérer s'il doit être exposé.

0 votes

@Goran Bien que je sois d'accord que ce ne serait pas idéal, cela n'échoue pas complètement car les accesseurs get/set vous permettent d'ajouter une logique supplémentaire si les champs sous-jacents changent par exemple.

50voto

Abel Points 24335

De nombreuses réponses ont déjà été ajoutées à ce fil de discussion intéressant, mais je n'ai pas trouvé la véritable raison pour laquelle il est nécessaire d'agir. pourquoi ce comportement est ce qu'il est. Laissez-moi faire un essai :

A l'époque

Quelque part entre Smalltalk dans les années 80 et Java au milieu des années 90, le concept d'orientation objet a mûri. Le masquage de l'information, qui n'était pas considéré à l'origine comme un concept propre à l'OO (mentionné pour la première fois en 1978), a été introduit dans Smalltalk : toutes les données (champs) d'une classe sont privées, toutes les méthodes sont publiques. Au cours des nombreux développements de l'OO dans les années 90, Bertrand Meyer a tenté de formaliser la plupart des concepts de l'OO dans son ouvrage de référence, intitulé Construction de logiciels orientés objet (OOSC) qui est depuis lors considéré comme une référence (presque) définitive sur les concepts OO et la conception des langages.

Dans le cas de la visibilité privée

Selon Meyer, une méthode doit être mise à la disposition d'un ensemble défini de classes (page 192-193). Cela donne évidemment une granularité très élevée de masquage de l'information, la fonction suivante est disponible pour les classesA etB et tous leurs descendants :

feature {classA, classB}
   methodName

Dans le cas de private il dit ceci : sans déclarer explicitement qu'un type est visible par sa propre classe, vous ne pouvez pas accéder à cette fonctionnalité (méthode/champ) dans un appel qualifié. C'est-à-dire que si x est une variable, x.doSomething() n'est pas autorisé. L'accès non qualifié est autorisé, bien sûr, à l'intérieur de la classe elle-même.

En d'autres termes, pour autoriser l'accès à une instance de la même classe, vous devez autoriser explicitement l'accès à la méthode par cette classe. C'est ce qu'on appelle parfois "instance-privée" ou "classe-privée".

Instance-privée dans les langages de programmation

Je connais au moins deux langages actuellement utilisés qui utilisent le masquage d'informations privé par instance par opposition au masquage d'informations privé par classe. L'un est Eiffel, un langage conçu par Meyer, qui pousse l'OO à l'extrême. L'autre est Ruby, un langage beaucoup plus courant de nos jours. En Ruby, private signifie : "privé à cette instance" .

Choix pour la conception de la langue

Il a été suggéré que l'autorisation de l'instance-private serait difficile pour le compilateur. Je ne le pense pas, car il est relativement simple d'autoriser ou d'interdire les appels qualifiés aux méthodes. Si pour une méthode privée, doSomething() est autorisé et x.doSomething() ne l'est pas, le concepteur du langage a effectivement défini l'accessibilité par instance uniquement pour les méthodes et les champs privés.

D'un point de vue technique, il n'y a pas de raison de choisir une voie ou l'autre (surtout si l'on considère que Eiffel.NET peut faire cela avec IL, même avec l'héritage multiple, il n'y a pas de raison inhérente de ne pas fournir cette fonctionnalité).

Bien sûr, c'est une question de goût et comme d'autres l'ont déjà mentionné, certaines méthodes pourraient être plus difficiles à écrire sans la fonctionnalité de visibilité des méthodes et champs privés au niveau de la classe.

Pourquoi C# n'autorise que l'encapsulation de classe et non l'encapsulation d'instance ?

Si vous regardez les fils de discussion sur Internet concernant l'encapsulation d'instance (un terme parfois utilisé pour faire référence au fait qu'un langage définit les modificateurs d'accès au niveau de l'instance, par opposition au niveau de la classe), le concept est souvent désapprouvé. Cependant, si l'on considère que certains langages modernes utilisent l'encapsulation d'instance, au moins pour le modificateur d'accès privé, on peut penser qu'il peut être et est utile dans le monde de la programmation moderne.

Cependant, il faut reconnaître que C# a regardé de plus près C++ et Java pour la conception de son langage. Eiffel et Modula-3 étaient également dans le coup, mais compte tenu des nombreuses caractéristiques manquantes d'Eiffel (héritage multiple), je pense qu'ils ont choisi la même voie que Java et C++ en ce qui concerne le modificateur d'accès privé.

Si vous voulez vraiment connaître le pourquoi vous devriez essayer de mettre la main sur Eric Lippert, Krzysztof Cwalina, Anders Hejlsberg ou toute autre personne ayant travaillé sur le standard de C#. Malheureusement, je n'ai pas pu trouver de note définitive dans la version annotée de la norme C#. Le langage de programmation C# .

1 votes

C'est une belle réponse avec beaucoup de détails, très intéressante, merci de prendre le temps de répondre à une question (relativement) ancienne.

1 votes

@RichK : de rien, j'ai pris connaissance de la question trop tard, je suppose, mais j'ai trouvé le sujet suffisamment fascinant pour y répondre de manière approfondie :)

0 votes

Sous COM, "privé" signifiait "privé par instance". Je pense que c'était dans une certaine mesure parce qu'une définition de la classe Foo représentaient effectivement une interface et une classe d'implémentation ; puisque COM n'avait pas de concept de référence classe-objet - seulement une référence d'interface - il n'y avait aucun moyen pour une classe de détenir une référence à quelque chose qui était garanti comme étant une autre instance de cette classe. Cette conception basée sur l'interface a rendu certaines choses encombrantes, mais elle signifiait que l'on pouvait concevoir une classe qui était substituable à une autre sans qu'elle ait à partager les mêmes éléments internes.

18voto

FishBasketGordo Points 14957

Ce n'est que mon opinion, mais de manière pragmatique, je pense que si un programmeur a accès à la source d'une classe, vous pouvez raisonnablement lui faire confiance pour accéder aux membres privés de l'instance de la classe. Pourquoi lier la main droite d'un programmeur alors que vous lui avez déjà donné les clés du royaume dans sa main gauche ?

13 votes

Je ne comprends pas cet argument. Disons que vous travaillez sur un programme où vous Vous êtes le seul développeur et vous êtes le seul à utiliser JAMAIS vos bibliothèques, dites-vous que dans ce cas vous rendez TOUS les membres publics parce que vous avez accès au code source ?

0 votes

@aquinas : c'est très différent. Rendre tout public conduira à des pratiques de programmation horribles. Permettre à un type de voir les champs privés d'autres instances de lui-même est un cas très étroit.

2 votes

Non, je ne préconise pas de rendre tous les membres publics. Ciel, non ! Mon argument est le suivant : si vous êtes déjà dans la source, que vous pouvez voir les membres privés, comment ils sont utilisés, les conventions employées, etc., alors pourquoi ne pas être en mesure d'utiliser ces connaissances ?

13voto

Lorenzo Dematté Points 3082

La raison en est le contrôle d'égalité, la comparaison, le clonage, la surcharge des opérateurs... Il serait très délicat d'implémenter l'opérateur+ sur des nombres complexes, par exemple.

3 votes

Mais tout ce que vous mentionnez nécessite qu'un type obtienne la valeur d'un autre type. Je suis d'accord pour dire : vous voulez inspecter mon état privé ? Très bien, allez-y. Vous voulez set mon état privé ? Pas question, mon pote, change ton propre état. :)

2 votes

@aquinas Cela ne fonctionne pas de cette façon. Les champs sont des champs. Ils ne sont en lecture seule que s'ils sont déclarés en tant que readonly et cela s'applique également à l'instance déclarante. De toute évidence, vous affirmez qu'il devrait y avoir une règle de compilation spécifique pour interdire ceci, mais chaque règle de ce type est une fonctionnalité que l'équipe C# doit spécifier, documenter, localiser, tester, maintenir et supporter. S'il n'y a pas d'obligation avantage alors pourquoi le feraient-ils ?

0 votes

Je suis d'accord avec @Aaronaught : il y a un coût associé à la mise en œuvre de chaque nouvelle spécification et modification du compilateur. Pour un scénario fixe, considérez une méthode Clone. Bien sûr, vous pouvez vouloir la réaliser avec un constructeur privé, mais peut-être que dans certains scénarios, ce n'est pas possible/conseillé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X