Pourquoi devrais-je éviter d'utiliser des propriétés en C#?
Dans son excellent livre, CLR Via C#, Jeffrey Richter a dit 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 les propriétés? En C # 3.0, avec des propriétés automatiques, cela change-t-il?
Comme 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 accessible en écriture. Si vous définissez un propriété, il est préférable d'offrir à la fois obtenir et définir des méthodes d'accesseur.
• une méthode de propriété peut lancer une exception; field access ne lève jamais une exception.
• une propriété ne peut pas être transmise en tant que paramètre out ou ref à une méthode; un champ peut le faire. Pour 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 beaucoup de temps à exécuter; l'accès aux champs se termine toujours immédiatement. Une raison courante d'utiliser les propriétés est d'effectuer la synchronisation des threads, qui peut arrêter le thread pour toujours, et par conséquent, une propriété ne doit pas être utilisée si la synchronisation du thread est requis. Dans cette situation, une méthode est préférée. Aussi, si votre classe peut être accessible à distance (par exemple, votre classe est dérivée du système.MashalByRefObject), l'appel de la méthode de propriété sera très lent, et par conséquent, une méthode est préférable à un propriété. À mon avis, les classes dérivées de MarshalByRefObject ne devraient jamais utiliser propriété.
* Si appelé plusieurs fois dans une ligne, une méthode de propriété peut renvoyer une valeur différente chaque temps; un champ renvoie la même valeur à chaque fois. système.La classe DateTime a un readonly Maintenant propriété qui renvoie la date et l'heure actuelles. Chaque fois que vous interrogez ceci propriété, il retournera une valeur différente. C'est une erreur, et Microsoft souhaite qu'ils pourrait corriger la classe en faisant Maintenant une méthode au lieu d'une propriété.
• une méthode de propriété peut provoquer des effets secondaires observables; l'accès aux champs ne le fait jamais. Dans d'autres mots, un l'utilisateur d'un type doit pouvoir définir diverses propriétés définies par un type dans tout ordre qu'il ou elle choisit sans remarquer aucun comportement différent dans le type.
• une méthode de propriété peut nécessiter une mémoire supplémentaire ou renvoyer une référence à quelque chose cela ne fait pas réellement partie de l'état de l'objet, donc la modification de l'objet retourné n'a pas effet sur l'objet d'origine; l'interrogation d'un champ retourne toujours une référence à un objet qui est garanti pour faire 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 est souvent ce n'est pas documenté.
14 réponses
La raison pour laquelle Jeff n'aime pas les propriétés est parce qu'elles ressemblent à des champs-donc les développeurs qui ne comprennent pas la différence les traiteront comme s'ils étaient des champs, en supposant qu'ils seront bon marché à exécuter, etc.
Personnellement, je suis en désaccord 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 suis d'accord que les développeurs doivent savoir que les propriétés sont essentiellement des méthodes déguisées-mais je pense que l'éducation les développeurs à ce sujet sont mieux que de rendre le code plus difficile à lire en utilisant des méthodes. (En particulier, ayant vu du code Java avec plusieurs getters et setters appelés dans la même déclaration, 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 pas vraiment tout cela.)
C'est un peu comme les arguments contre l'utilisation de 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.
Eh bien, prenons ses arguments un par un:
Une propriété peut être en lecture seule ou en écriture seule; l'accès aux champs est toujours lisible et inscriptible.
C'est une victoire pour les propriétés, puisque vous avez un contrôle d'accès plus fin.
Une méthode de propriété peut lancer un exception; l'accès au champ ne lance jamais exception.
Bien que cela soit généralement vrai, vous pouvez très bien appeler une méthode sur un champ d'objet non initialisé et avoir une exception jetée.
• une propriété ne peut pas être transmise ou ref paramètre à une méthode; un le terrain peut.
Juste.
• Une méthode de propriété peut prendre beaucoup de temps d'exécution; accès aux champs toujours termine immédiatement.
Cela peut aussi prendre très peu de temps.
* si elle est appelée plusieurs fois de suite, méthode de propriété peut renvoyer un autre valeur à chaque fois; un champ renvoie même valeur à chaque fois.
Pas vrai. Comment savez-vous que la valeur du champ n'a pas changé (peut-être par un autre thread)?
Le Système.La classe DateTime a un readonly Now propriété qui renvoie le date et l'heure actuelles. Chaque fois que vous requête cette propriété, elle renverra un valeur différente. C'est une erreur, et Microsoft souhaite qu'ils puissent corriger la classe en faisant Maintenant une méthode au lieu d'une propriété.
Si c'est une erreur, c'est un mineur.
• Une méthode de propriété peut provoquer effets secondaires observables; accès sur le terrain ne le fait jamais. En d'autres termes, un utilisateur de un type devrait être capable de définir divers propriétés définies par un type dans ordre qu'il ou elle choisit sans remarquant tout comportement différent dans le type.
Juste.
• Une méthode de propriété peut exiger mémoire supplémentaire ou retour a référence à quelque chose qui n'est pas fait partie de l'état de l'objet, donc, la modification de l'objet retourné a aucun effet sur l'objet d'origine; interrogation d'un champ retourne toujours une référence à un objet qui est garanti pour faire partie de l'original de l'objet d'état. Travailler avec un propriété qui renvoie une copie peut être très déroutant pour les développeurs, et ce caractéristique est souvent pas documenter.
La plupart des protestations pourraient être dites pour les getters et les setters de Java aussi-et nous les avons eu pendant un certain temps sans de tels problèmes dans la pratique.
Je pense que la plupart des problèmes pourraient être résolus par mieux coloration syntaxique (c'est-à-dire différencier les propriétés des champs) afin que le programmeur sache à quoi s'attendre.
Je n'ai pas lu le livre, et vous n'avez pas cité la partie de celui-ci que vous ne comprenez pas, donc je vais devoir deviner.
Certaines personnes n'aiment pas les propriétés car elles peuvent faire en sorte que votre code fasse des choses surprenantes.
Si je tape Foo.Bar
, les personnes qui le lisent s'attendent normalement à ce que cela accède simplement à un champ Membre de la classe Foo. C'est une opération bon marché, presque gratuite, et c'est déterministe. Je peux l'appeler à plusieurs reprises, et obtenir le même résultat à chaque fois.
À la place, avec propriétés, il pourrait en fait être un appel de fonction. Ce pourrait être une boucle infinie. Elle pourrait ouvrir une connexion de base de données. Il peut renvoyer des valeurs différentes chaque fois que j'y accède.
C'est un argument similaire à la raison pour laquelle Linus déteste C++. Votre code peut agir surprenant pour le lecteur. Il déteste la surcharge de l'opérateur: a + b
ne signifie pas nécessairement une simple addition. Cela peut signifier une opération extrêmement compliquée, tout comme les propriétés C#. Il peut avoir des effets secondaires. Il peut faire n'importe quoi.
Honnêtement, Je pense que c'est un argument faible. Les deux langues sont pleines de choses comme ça. (Devrions-nous éviter la surcharge de l'opérateur en C# aussi? Après tout, le même argument peut être utilisé)
Les propriétésPermettent l'abstraction. Nous pouvons prétendre {[17] } que quelque chose est un champ régulier, et l'utiliser comme si c'était un, et ne pas avoir à se 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 devraient 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 parfaits. Ils ne peuvent pas être passés 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 transmises en tant que délégués, permettant à une fonction appelée de accéder directement.
Propriétés... Je ne peux pas.
Qui suce.
Mais cela ne signifie pas que les propriétés sont mauvaises ou ne devraient pas être utilisées. Pour de nombreuses raisons, ils sont grands.
Retour en 2009, ce conseil semblait simplement comme bellyaching de la qui a déplacé mon fromage variété. Aujourd'hui, c'est presque laughably obsolètes.
Un point très important que beaucoup de réponses semblent sur la pointe des pieds mais ne traitent pas tout à fait de front est que ces prétendus "dangers" des propriétés sont une partie intentionnelle 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. Ce est un avantage sur les champs. Un modèle commun est d'avoir un getter public et un setterprotégé ouinterne , une technique d'héritage très utile qui n'est pas réalisable par les champs seuls.
Jetez une exception. À ce jour, cela reste l'une des méthodes de validation les plus efficaces, en particulier lorsque vous travaillez avec des frameworks D'interface utilisateur qui impliquent des concepts de liaison de données. Il est beaucoup plus difficile de s'assurer qu'un objet reste dans un état valide lorsque travailler avec les champs.
Prenez beaucoup de temps à exécuter. La comparaison valide ici est avec méthodes , qui prennent aussi longtemps - pas champs . Aucune base n'est donnée pour l'énoncé "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 semble presque comme une blague à proximité du point vantant les vertus de
ref
/out
paramètres avec champs, dont la valeur d'un champ après unref
/out
l'appel est à peu près garanti pour être différent de sa valeur précédente, et de façon imprévisible.Si nous parlons du cas spécifique (et pratiquement académique) de l'accès mono-thread sans couplages afférents, il est assez bien compris que c'est juste une mauvaise conception de propriété pour avoir des effets secondaires qui changent d'état visible, et peut-être que ma mémoire s'estompe, mais je n'arrive tout simplement pas à me]} et s'attendre à ce que la même valeur sorte à chaque fois. Au moins pas tous les cas où ils ne l'auraient pas tout aussi mal foiré avec un hypothétique
DateTime.Now()
. -
Provoque 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. Les propres directives Property Design de Microsoft indiquent que l'ordre du setter ne devrait pas avoir d'importance, car autrement impliquerait couplage temporel. Pour sûr, vous ne pouvez pas réaliser de couplage temporel avec les champs seuls, mais c'est seulement parce que vous ne pouvez pas provoquer de comportement significatif avec les champs seuls, jusqu'à ce qu'une méthode soit exécutée.
Accesseurs de Propriété peut effectivement aider empêcher certains types de couplage temporel en forçant l'objet dans un état valide avant toute prise de mesures - par exemple, si une classe a un
StartDate
etEndDate
, alors la configuration de laEndDate
avantStartDate
pourrait forcer laStartDate
retour. C'est vrai même dans les environnements multithread ou asynchrones, y compris l'exemple évident d'une interface utilisateur axée sur les événements.
Autres choses que les propriétés peuvent faire et que les champs ne peuvent pas inclure:
- chargement paresseux , l'un des moyens les plus efficaces de prévenir les erreurs d'ordre d'initialisation.
- Les Notifications de modification , qui constituent à peu près toute la base de l'architecture MVVM.
-
Héritage, par exemple, la définition d'un résumé
Type
ouName
ainsi les classes dérivées peuvent fournir des métadonnées intéressantes mais néanmoins constantes sur elles-mêmes. - Interception , grâce à ce qui précède.
-
Indexers , que tous ceux qui ont déjà eu à travailler avec COM interop et l'inévitable crachat d'appels
Item(i)
reconnaîtront comme une chose merveilleuse. - travaillez avec PropertyDescriptor qui est essentiel pour créer des concepteurs et pour les frameworks XAML dans général.
Richter est clairement un auteur prolifique et sait beaucoup de choses sur le CLR et le C#, mais je dois dire, il semble que lorsqu'il a écrit ce conseil (Je ne suis pas sûr que ce soit dans ses révisions plus récentes-j'espère sincèrement que non) il ne voulait pas lâcher les vieilles habitudes et avait du mal à accepter les conventions de C# (vs. 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 à un seul statement: Les propriétés ressemblent à des champs, mais elles peuvent ne pas agir comme des champs. et le problème avec la déclaration est, ce n'est pas vrai, ou au mieux c'est très trompeur. Les propriétés ne ressemblent pas à des champs - au moins, elles ne sont pas supposées ressembler à des champs.
Il y a deuxtrès fortes conventions de codage en C # avec des conventions similaires partagées par d'autres langages CLR, et FXCop vous criera si vous ne suivez pas les:
- Champs toujours être privé, jamais public.
- les champs doivent être déclarés dans camelCase. Les propriétés sont PascalCase.
Ainsi, il n'y a aucune ambiguïté sur le fait que Foo.Bar = 42
soit un accesseur de propriété ou un accesseur de champ. C'est un accesseur de propriété et devrait être traité comme toute autre méthode-il pourrait être lent, il pourrait lancer une exception,etc. C'est la nature de Abstraction - c'est entièrement à la discrétion de la classe déclarant comment réagir. Les concepteurs de classe devraient appliquer le principe de la moindre surprise, mais les appelants ne devraient rien assumer à propos d'une propriété, sauf qu'il fait ce qu'il dit sur le tin. c'est exprès.
L'alternative aux propriétés est les méthodes getter/setter partout. C'est L'approche Java, et elle a été controversée depuis le début. C'est bien si c'est votre sac, mais ce n'est pas comme ça que nous roulons dans le camp.net. Nous essayons, au moins dans les limites d'un système statiquement typé, pour éviter ce que Fowler appelle bruit syntaxique . Nous ne voulons pas de parenthèses supplémentaires, supplémentaires get
/set
verrues, ou signatures de méthode supplémentaires - Pas si nous pouvons les éviter sans perte de clarté.
Dites ce que vous voulez, mais {[17] } sera toujours beaucoup plus facile à lire que foo.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 à ce qui précède paragraphe est de dire, "bien sûr, il est difficile de lire, mais il 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.)
Je ne vois aucune raison pour laquelle vous ne devriez pas utiliser les propriétés en général.
Les propriétés automatiques en C # 3 + simplifient un peu la syntaxe (a la syntatic sugar).
C'est juste l'opinion d'une personne. J'ai lu pas mal de livres c# et je n'ai pas encore vu quelqu'un d'autre dire "n'utilisez pas de 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 n'importe quel mécanisme vous le souhaitez. 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. En les utilisant et en les écrivant, je pense juste aux propriétés comme setters et getters qui sont beaucoup plus agréables syntaxe.
En ce qui concerne les mises en garde avec des propriétés, il y en a un couple. L'un est probablement une mauvaise utilisation de propriétés, l'autre peut être subtile.
Premièrement, les propriétés sont des types de méthodes. Cela peut être surprenant si vous placez une logique compliquée dans une propriété car la plupart des utilisateurs d'une classe s'attendent à ce que la propriété soit assez légère.
Par exemple
public class WorkerUsingMethod
{
// Explicitly obvious that calculation is being done here
public int CalculateResult()
{
return ExpensiveLongRunningCalculation();
}
}
public class WorkerUsingProperty
{
// Not at all obvious. Looks like it may just be returning a cached result.
public int Result
{
get { return ExpensiveLongRunningCalculation(); }
}
}
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 pendant le débogage.
Dites que vous avez une propriété comme celle-ci:
public int Result
{
get
{
m_numberQueries++;
return m_result;
}
}
Supposons maintenant que vous ayez une exception qui se produit lorsque trop de requêtes sont faites. Devinez ce qui se passe lorsque vous démarrez le débogage et le renversement de la propriété dans le débogueur? De mauvaises choses. Évitez de faire cela! Regarder la propriété change 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 de loin sur le problème.
Cette raison doit avoir été 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 qu'ils vous donnent un niveau d'abstraction permettant de changer le comportement d'une classe sans affecter ses clients...
Je ne peux m'empêcher de choisir les détails des 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 accessible en écriture.
Faux: les champs peuvent être marqués en lecture seule afin que seul le constructeur de l'objet puisse y écrire.
Une méthode de propriété peut lancer une exception; field access ne lève jamais une exception.
Faux: l'implémentation d'une classe peut changer le modificateur d'accès d'un champ de public à privé. Les tentatives de lecture de champs privés au moment de l'exécution entraîneront toujours une exception.
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:
- il n'y a pas d'opération fastidieuse à l'intérieur de la propriété getter / setter
- le getter de propriété n'a pas d'effets secondaires (l'appelant plusieurs fois, ne change pas le résultat)
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 ça nécessite une certaine éducation.
Il y a un moment où je considère ne pas utiliser les 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és simples, donc dans ce cas, l'ajout d'une propriété simple provoque une petite quantité de code sur l'utilisation d'un champ public. Habituellement, ce ne serait pas un problème, mais presque toujours dans le monde du Framework Compact, vous êtes confronté à des contraintes de mémoire serrées, donc même minuscules les économies comme celle-ci comptent.
Vous ne devriez pas éviter de les utiliser mais vous devriez les utiliser avec qualification et soin, pour les raisons données par d'autres contributeurs.
J'ai déjà vu une propriété appelée quelque chose comme des clients qui ont 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 pour les Clients.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 que 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 utiliser les propriétés est qu'elles vous permettent de valider la valeur définie. Une autre est que la valeur de la propriété peut être une valeur dérivée, pas un seul champ, comme TotalValue = amount * quantity.
Personnellement, je n'utilise que les propriétés lors de la création de méthodes get / set simples. Je m'en éloigne en arrivant à des structures de données compliquées.
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 expectactions des programmeurs. net:
les Propriétés ne ressemblent pas à des champs. Les champs ressemblent à des propriétés.
• une méthode de propriété peut lancer une exception; field access ne lève jamais une exception.
Donc, un champ est comme une propriété qui est garantie de ne jamais lancer d'exception.
* une propriété ne peut pas être transmis en tant que paramètre out ou ref à une méthode; un champ peut.
Donc, un champ est comme une propriété, mais il a des capacités supplémentaires: passer à un ref
/out
accepter les méthodes.
• une méthode de propriété peut prendre beaucoup de temps à exécuter; l'accès aux champs se termine toujours immédiatement. [...]
Donc, un champ est comme une propriété rapide.
* 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. système.La classe DateTime a une propriété ReadOnly Now qui renvoie la date et l'heure actuelles.
Ainsi, un champ est comme une propriété qui est garantie de retourner la même valeur, sauf si le champ a été défini sur une valeur différente.
• une méthode de propriété peut provoquer des effets secondaires observables; l'accès aux champs ne le fait jamais.
Encore une fois, un champ est une propriété qui est garantie de ne pas le faire.
• Une méthode de propriété peut besoin de mémoire supplémentaire ou de renvoyer une référence à quelque chose qui ne fait pas partie de l'état de l'objet, de sorte que la modification de l'objet retourné n'a pas d'effet sur l'objet d'origine; l'interrogation d'un champ retourne toujours une référence à un objet qui est garanti pour faire partie de l'objet d'origine de l'état. Travailler avec une propriété qui renvoie une copie peut être très déroutant pour les développeurs, et cette caractéristique est souvent pas documenté.
Celui-ci peut-être surprenant, mais pas parce que c'est une propriété qui fait cela. Au contraire, c'est que presque personne ne renvoie des copies mutables dans les propriétés, de sorte que 0.1% case est surprenant.
L'appel de méthodes au lieu de propriétés réduit considérablement la lisibilité du code d'appel. Dans J#, par exemple, en utilisant ADO.NET était un cauchemar parce que Java ne supporte pas 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.