Dans son excellent livre, CLR Via C #, Jeffrey Richter a déclaré qu'il n'aime pas les propriétés et recommande de ne pas les utiliser. Il a donné une raison, mais je ne comprends pas vraiment. Quelqu'un peut-il m'expliquer pourquoi je devrais ou ne devrais pas utiliser des propriétés? En C # 3.0, avec les propriétés automatiques, cela change-t-il?
À titre de référence, j'ai ajouté les opinions de Jeffrey Richter:
• Une propriété peut être en lecture seule ou en écriture seule; l'accès aux champs est toujours lisible et inscriptible. Si vous définissez une propriété, il est préférable de proposer à la fois des méthodes d'accesseur get et set.
• Une méthode de propriété peut lever une exception; l'accès aux champs ne lève jamais d'exception.
• Une propriété ne peut pas être transmise en tant que paramètre out ou ref à une méthode; un champ peut. Par exemple, le code suivant ne sera pas compilé:
using System;
public sealed class SomeType
{
private static String Name
{
get { return null; }
set {}
}
static void MethodWithOutParam(out String n) { n = null; }
public static void Main()
{
// For the line of code below, the C# compiler emits the following:
// error CS0206: A property or indexer may not
// be passed as an out or ref parameter
MethodWithOutParam(out Name);
}
}
• Une méthode de propriété peut prendre un certain temps à s'exécuter; l'accès au champ se termine toujours immédiatement. Une raison courante d'utiliser des propriétés est d'effectuer la synchronisation des threads, ce qui peut arrêter le thread pour toujours, et par conséquent, une propriété ne doit pas être utilisée si la synchronisation des threads est requise. Dans cette situation, une méthode est préférée. En outre, si votre classe est accessible à distance (par exemple, votre classe est dérivée de System.MashalByRefObject), l'appel de la méthode de propriété sera très lent et, par conséquent, une méthode est préférée à une propriété. À mon avis, les classes dérivées de MarshalByRefObject ne devraient jamais utiliser de propriétés.
• Si elle est appelée plusieurs fois de suite, une méthode de propriété peut renvoyer une valeur différente à chaque fois; un champ renvoie la même valeur à chaque fois. La classe System.DateTime a une propriété en lecture seule Now qui renvoie la date et l'heure actuelles. Chaque fois que vous interrogez cette propriété, elle renvoie une valeur différente. C'est une erreur, et Microsoft souhaite pouvoir corriger la classe en faisant de Now une méthode au lieu d'une propriété.
• Une méthode de propriété peut provoquer des effets secondaires observables; l'accès sur le terrain ne le fait jamais. En d'autres termes, un utilisateur d'un type doit être capable de définir diverses propriétés définies par un type dans n'importe quel ordre de son choix sans remarquer aucun comportement différent dans le type.
• Une méthode de propriété peut nécessiter de la mémoire supplémentaire ou renvoyer une référence à quelque chose qui ne fait pas réellement partie de l'état de l'objet, donc la modification de l'objet retourné n'a aucun effet sur l'objet d'origine; l'interrogation d'un champ renvoie toujours une référence à un objet dont il est garanti qu'il fait partie de l'état de l'objet d'origine. Travailler avec une propriété qui renvoie une copie peut être très déroutant pour les développeurs, et cette caractéristique n'est souvent pas documentée.
la source
Réponses:
La raison pour laquelle Jeff n'aime pas les propriétés est qu'elles ressemblent à des champs - donc les développeurs qui ne comprennent pas la différence les traiteront comme s'il s'agissait de champs, en supposant qu'ils seront bon marché à exécuter, etc.
Personnellement, je ne suis pas d'accord avec lui sur ce point particulier - je trouve que les propriétés rendent le code client beaucoup plus simple à lire que les appels de méthode équivalents. Je conviens que les développeurs doivent savoir que les propriétés sont essentiellement des méthodes déguisées - mais je pense qu'éduquer les développeurs à ce sujet est mieux que de rendre le code plus difficile à lire à l'aide de méthodes. (En particulier, ayant vu du code Java avec plusieurs getters et setters appelés dans la même instruction, je sais que le code C # équivalent serait beaucoup plus simple à lire. La loi de Demeter est très bien en théorie, mais parfois
foo.Name.Length
est vraiment la bonne chose à utiliser ...)(Et non, les propriétés implémentées automatiquement ne changent rien de tout cela.)
C'est un peu comme les arguments contre l'utilisation des méthodes d'extension - je peux comprendre le raisonnement, mais l'avantage pratique (lorsqu'il est utilisé avec parcimonie) l'emporte sur l'inconvénient à mon avis.
la source
Eh bien, prenons ses arguments un par un:
C'est une victoire pour les propriétés, car vous disposez d'un contrôle d'accès plus fin.
Bien que cela soit généralement vrai, vous pouvez très bien appeler une méthode sur un champ objet non initialisé et faire lever une exception.
Juste.
Cela peut également prendre très peu de temps.
Pas vrai. Comment savez-vous que la valeur du champ n'a pas changé (peut-être par un autre thread)?
Si c'est une erreur, c'est mineure.
Juste.
La plupart des protestations pourraient également concerner les getters et les setters de Java - et nous les avons eues pendant un bon moment sans de tels problèmes dans la pratique.
Je pense que la plupart des problèmes pourraient être résolus par une meilleure coloration syntaxique (c'est-à-dire en différenciant les propriétés des champs) afin que le programmeur sache à quoi s'attendre.
la source
_field
. Ou même simplement en minusculesfield
.Je n'ai pas lu le livre et vous n'en avez pas cité la partie que vous ne comprenez pas, alors je vais devoir deviner.
Certaines personnes n'aiment pas les propriétés car elles peuvent amener votre code à faire des choses surprenantes.
Si je tape
Foo.Bar
, les gens qui le liront s'attendront normalement à ce qu'il accède simplement à un champ membre de la classe Foo. C'est une opération bon marché, presque gratuite et déterministe. Je peux l'appeler encore et encore et obtenir le même résultat à chaque fois.Au lieu de cela, avec les propriétés, il peut s'agir d'un appel de fonction. Ce pourrait être une boucle infinie. Cela pourrait ouvrir une connexion à la base de données. Il peut renvoyer des valeurs différentes chaque fois que j'y accède.
C'est un argument similaire pour expliquer pourquoi Linus déteste le C ++. Votre code peut surprendre le lecteur. Il déteste la surcharge des opérateurs:
a + b
ne signifie pas nécessairement un simple ajout. Cela peut signifier une opération extrêmement compliquée, tout comme les propriétés C #. Cela peut avoir des effets secondaires. Cela peut faire n'importe quoi.Honnêtement, je pense que c'est un argument faible. Les deux langues regorgent de choses comme celle-ci. (Devrions-nous éviter la surcharge d'opérateurs en C # également? Après tout, le même argument peut être utilisé ici)
Les propriétés permettent l'abstraction. Nous pouvons prétendre que quelque chose est un domaine normal, l'utiliser comme s'il en était un, sans avoir à nous soucier de ce qui se passe dans les coulisses.
C'est généralement considéré comme une bonne chose, mais cela repose évidemment sur l'écriture d'abstractions significatives par le programmeur. Vos propriétés doivent se comporter comme des champs. Ils ne devraient pas avoir d'effets secondaires, ils ne devraient pas effectuer d'opérations coûteuses ou dangereuses. Nous voulons pouvoir les considérer comme des champs.
Cependant, j'ai une autre raison de les trouver moins que parfaites. Ils ne peuvent pas être transmis par référence à d'autres fonctions.
Les champs peuvent être passés comme
ref
, permettant à une fonction appelée d'y accéder directement. Les fonctions peuvent être passées en tant que délégués, permettant à une fonction appelée d'y accéder directement.Les propriétés ... ne peuvent pas.
Ça craint.
Mais cela ne signifie pas que les propriétés sont mauvaises ou ne doivent pas être utilisées. À de nombreuses fins, ils sont excellents.
la source
ref
paramètres; un membre (par exempleFoo
) d'un formulairevoid Foo<T>(ActionByRef<Point,T> proc, ref T param)
avec un attribut spécial, et faire transformer le compilateurthing.Foo.X+=someVar;
enFoo((ref Point it, ref int param)=>it.X += param, ref someVar);
. Puisque le lambda est un délégué statique, aucune fermeture ne serait requise, et l'utilisateur d'un objet pourrait utiliser n'importe quel emplacement de stockage sauvegardé la propriété en tant queref
paramètre authentique (et pourrait le transmettre à d'autres méthodes en tant queref
paramètre).En 2009, ce conseil ressemblait simplement à la variété Who Moved My Cheese . Aujourd'hui, c'est presque ridiculement obsolète.
Un point très important que de nombreuses réponses semblent sur la pointe des pieds mais qui ne sont pas tout à fait abordés de front est que ces prétendus «dangers» des propriétés font intentionnellement partie de la conception du cadre!
Oui, les propriétés peuvent:
Spécifiez différents modificateurs d'accès pour le getter et le setter. C'est un avantage par rapport aux champs. Un modèle courant est d'avoir un getter public et un setter protégé ou interne , une technique d'héritage très utile qui n'est pas réalisable par des champs seuls.
Lancez une exception. À ce jour, cela reste l'une des méthodes de validation les plus efficaces, en particulier lorsque vous travaillez avec des cadres d'interface utilisateur qui impliquent des concepts de liaison de données. Il est beaucoup plus difficile de garantir qu'un objet reste dans un état valide lorsque vous travaillez avec des champs.
Prenez beaucoup de temps à exécuter. La comparaison valable ici est avec les méthodes , qui prennent tout aussi longtemps - pas les champs . Aucune base n'est donnée pour la déclaration «une méthode est préférée» autre que la préférence personnelle d'un auteur.
Renvoie des valeurs différentes de son getter lors des exécutions suivantes. Cela ressemble presque à une blague si proche du point vantant les vertus de
ref
/out
parameters avec des champs, dont la valeur d'un champ après un appelref
/out
est à peu près garantie d'être différente de sa valeur précédente, et de manière imprévisible.Si nous parlons du cas spécifique (et pratiquement académique) de l'accès monotrou sans accouplements afférents, c'est assez bien entendu que c'est juste une mauvaise conception de propriété d'avoir des effets secondaires de changement d'état visible, et peut-être que ma mémoire est décoloration, mais je n'arrive tout simplement pas à me souvenir d'exemples de personnes utilisant
DateTime.Now
et s'attendant à ce que la même valeur ressorte à chaque fois. Du moins, pas de cas où ils n'auraient pas tout foiré avec un hypothétiqueDateTime.Now()
.Causer des effets secondaires observables - ce qui est bien sûr précisément la raison pour laquelle les propriétés ont été inventées en tant que caractéristique du langage en premier lieu. Propre à Microsoft directives de conception de propriétés de indiquent que l'ordre des passeurs ne devrait pas avoir d'importance, car faire autrement impliquerait un couplage temporel . Certes, vous ne pouvez pas réaliser un couplage temporel avec des champs seuls, mais c'est uniquement parce que vous ne pouvez pas provoquer de comportement significatif avec des champs seuls, jusqu'à ce qu'une méthode soit exécutée.
Les accesseurs de propriété peuvent en fait aider à empêcher certains types de couplage temporel en forçant l'objet dans un état valide avant qu'une action ne soit entreprise - par exemple, si une classe a un
StartDate
et unEndDate
, alors définir leEndDate
avant leStartDate
pourrait également forcer leStartDate
retour. Cela est vrai même dans les environnements multithreads ou asynchrones, y compris l'exemple évident d'une interface utilisateur événementielle.Autres choses que les propriétés peuvent faire et que les champs ne peuvent pas inclure:
Type
ou desName
classes dérivées, peut fournir des métadonnées intéressantes mais néanmoins constantes sur elles-mêmes.Item(i)
appels reconnaîtront comme une chose merveilleuse.Richter est clairement un auteur prolifique et en sait beaucoup sur le CLR et le C #, mais je dois dire qu'il semble que lorsqu'il a écrit ce conseil à l'origine (je ne suis pas sûr que ce soit dans ses révisions les plus récentes - j'espère sincèrement que non) qu'il ne voulait tout simplement pas abandonner ses vieilles habitudes et avait du mal à accepter les conventions de C # (contre C ++, par exemple).
Ce que je veux dire par là, c'est que son argument «propriétés considérées comme nuisibles» se résume essentiellement à une seule déclaration: les propriétés ressemblent à des champs, mais elles peuvent ne pas agir comme des champs. Et le problème avec cette déclaration est que ce n'est pas vrai ou, au mieux, c'est très trompeur. Les propriétés ne ressemblent pas à des champs - du moins, elles ne sont pas censées ressembler à des champs.
Il existe deux conventions de codage très fortes en C # avec des conventions similaires partagées par d'autres langages CLR, et FXCop vous criera dessus si vous ne les suivez pas:
Ainsi, il n'y a aucune ambiguïté quant à savoir s'il
Foo.Bar = 42
s'agit d'un accesseur de propriété ou d'un accesseur de champ. C'est un accesseur de propriété et devrait être traité comme n'importe quelle autre méthode - cela pourrait être lent, cela pourrait lever une exception, etc. C'est la nature d' Abstraction - c'est à la discrétion de la classe déclarante de savoir comment réagir. Les concepteurs de classe doivent appliquer le principe de la moindre surprise, mais les appelants ne doivent rien supposer d'une propriété, sauf qu'elle fait ce qu'elle dit sur l'étain. C'est exprès.L'alternative aux propriétés est des méthodes getter / setter partout. C'est l'approche Java, et elle est controversée depuis le début . C'est bien si c'est votre sac, mais ce n'est tout simplement pas comme ça que nous roulons dans le camp .NET. Nous essayons, au moins dans les limites d'un système à typage statique, d'éviter ce que Fowler appelle Syntactic Noise . Nous ne voulons pas de parenthèses supplémentaires, d'extra
get
/set
warts ou de signatures de méthode supplémentaires - pas si nous pouvons les éviter sans perte de clarté.Dites ce que vous voulez, mais ce
foo.Bar.Baz = quux.Answers[42]
sera toujours beaucoup plus facile à lire quefoo.getBar().setBaz(quux.getAnswers().getItem(42))
. Et quand vous lisez des milliers de lignes de ceci par jour, cela fait une différence.(Et si votre réponse naturelle au paragraphe ci-dessus est de dire: "Bien sûr, c'est difficile à lire, mais ce serait plus facile si vous le divisez en plusieurs lignes", alors je suis désolé de dire que vous avez complètement manqué le point .)
la source
Je ne vois aucune raison pour laquelle vous ne devriez pas utiliser Propriétés en général.
Les propriétés automatiques en C # 3+ ne simplifient qu'un peu la syntaxe (à la manière du sucre syntatique).
la source
C'est juste l'opinion d'une personne. J'ai lu pas mal de livres c # et je n'ai encore vu personne d'autre dire "ne pas utiliser les propriétés".
Personnellement, je pense que les propriétés sont l'une des meilleures choses à propos de c #. Ils vous permettent d'exposer l'état via le mécanisme de votre choix. Vous pouvez instancier paresseusement la première fois que quelque chose est utilisé et vous pouvez faire la validation sur la définition d'une valeur, etc. Lors de leur utilisation et de leur écriture, je pense juste aux propriétés comme des setters et des getters qui ont une syntaxe beaucoup plus agréable.
En ce qui concerne les mises en garde concernant les propriétés, il y en a quelques-unes. L'une est probablement une mauvaise utilisation des propriétés, l'autre peut être subtile.
Premièrement, les propriétés sont des types de méthodes. Il peut être surprenant de placer une logique compliquée dans une propriété, car la plupart des utilisateurs d'une classe s'attendront à ce que la propriété soit assez légère.
Par exemple
Je trouve que l'utilisation de méthodes pour ces cas aide à faire une distinction.
Deuxièmement, et plus important encore, les propriétés peuvent avoir des effets secondaires si vous les évaluez lors du débogage.
Disons que vous avez une propriété comme celle-ci:
Supposons maintenant que vous ayez une exception qui se produit lorsque trop de requêtes sont effectuées. Devinez ce qui se passe lorsque vous démarrez le débogage et survolez la propriété dans le débogueur? Mauvaises choses. Évitez de faire ça! L'examen de la propriété modifie l'état du programme.
Ce sont les seules mises en garde que j'ai. Je pense que les avantages des propriétés l'emportent largement sur les problèmes.
la source
Cette raison a dû être donnée dans un contexte très spécifique. C'est généralement l'inverse - il est recommandé d'utiliser les propriétés car elles vous donnent un niveau d'abstraction vous permettant de changer le comportement d'une classe sans affecter ses clients ...
la source
Je ne peux m'empêcher de choisir les détails des opinions de Jeffrey Richter:
Mauvais: les champs peuvent être marqués en lecture seule afin que seul le constructeur de l'objet puisse y écrire.
Erreur: l'implémentation d'une classe peut changer le modificateur d'accès d'un champ de public à privé. Les tentatives de lecture des champs privés lors de l'exécution entraîneront toujours une exception.
la source
Je ne suis pas d'accord avec Jeffrey Richter, mais je peux deviner pourquoi il n'aime pas les propriétés (je n'ai pas lu son livre).
Même si les propriétés sont comme des méthodes (en termes d'implémentation), en tant qu'utilisateur d'une classe, je m'attends à ce que ses propriétés se comportent "plus ou moins" comme un champ public, par exemple:
Malheureusement, j'ai vu des propriétés qui ne se comportaient pas de cette façon. Mais le problème ne sont pas les propriétés elles-mêmes, mais les personnes qui les ont mises en œuvre. Donc, cela nécessite juste une certaine éducation.
la source
Il y a un moment où j'envisage de ne pas utiliser de propriétés, et c'est en écrivant du code .Net Compact Framework. Le compilateur CF JIT n'effectue pas la même optimisation que le compilateur JIT de bureau et n'optimise pas les accesseurs de propriété simples, donc dans ce cas, l'ajout d'une propriété simple provoque une petite quantité de code gonfler en utilisant un champ public. Habituellement, ce ne serait pas un problème, mais presque toujours dans le monde du Compact Framework, vous êtes confronté à des contraintes de mémoire strictes, donc même de minuscules économies comme celle-ci comptent.
la source
Vous ne devez pas éviter de les utiliser, mais vous devez les utiliser avec réserve et soin, pour les raisons données par d'autres contributeurs.
J'ai vu une fois une propriété appelée quelque chose comme Clients qui a ouvert en interne un appel hors processus à une base de données et lu la liste des clients. Le code client avait un 'for (int i to Customers.Count)' qui provoquait un appel séparé à la base de données à chaque itération et pour l'accès du client sélectionné. C'est un exemple flagrant qui démontre le principe de garder la propriété très légère - rarement plus qu'un accès au champ interne.
Un argument pour l'utilisation des propriétés est qu'elles vous permettent de valider la valeur définie. Une autre est que la valeur de de la propriété peut être une valeur dérivée, pas un seul champ, comme TotalValue = montant * quantité.
la source
Personnellement, je n'utilise les propriétés que lors de la création de méthodes simples get / set. Je m'en éloigne pour arriver à des structures de données compliquées.
la source
L'argument suppose que les propriétés sont mauvaises car elles ressemblent à des champs, mais peuvent faire des actions surprenantes. Cette hypothèse est invalidée par les attentes des programmeurs .NET:
Les propriétés ne ressemblent pas à des champs. Les champs ressemblent à des propriétés.
Ainsi, un champ est comme une propriété qui est garantie de ne jamais lever d'exception.
Ainsi, un champ est comme une propriété, mais il a des capacités supplémentaires: passer à une méthode
ref
/out
accepting.Ainsi, un champ est comme une propriété rapide.
Ainsi, un champ est comme une propriété qui est garantie de renvoyer la même valeur à moins que le champ n'ait été défini sur une valeur différente.
Encore une fois, un champ est une propriété qui est garantie de ne pas faire cela.
Celui-ci peut être surprenant, mais pas parce que c'est une propriété qui fait cela. C'est plutôt que presque personne ne retourne des copies mutables dans les propriétés, de sorte que le cas de 0,1% est surprenant.
la source
L'appel de méthodes au lieu de propriétés réduit considérablement la lisibilité du code d'appel. En J #, par exemple, utiliser ADO.NET était un cauchemar car Java ne prend pas en charge les propriétés et les indexeurs (qui sont essentiellement des propriétés avec des arguments). Le code résultant était extrêmement laid, avec des appels de méthode entre parenthèses vides partout.
La prise en charge des propriétés et des indexeurs est l'un des avantages de base de C # par rapport à Java.
la source