Dois-je préférer des propriétés avec ou sans champs privés?

16

La base de code dans laquelle je travaille a maintenant la convention d'utiliser des champs privés et des propriétés publiques. Par exemple, la plupart des classes ont leurs membres définis comme ceci:

// Fields
private double _foo;
private double _bar;
private double _baz;

// Properties
public double Foo
{
    get{ return _foo; }
    set{ _foo = value; }
}

public double Bar
{
    get{ return _bar; }
    set{ _bar = value; }
}

public double Baz
{
    get{ return _baz; }
}

Je sais que ceux-ci peuvent être réécrits sans leurs propriétés privées internes:

public double Foo{ get; set; }
public double Bar{ get; set; }
public double Baz{ get; private set; }

J'aimerais avoir quelques commentaires à ce sujet:

  • Y a-t-il une bonne raison de préférer le style plus ancien et plus explicite au nouveau, plus concis?
  • Dois-je écrire de nouvelles classes en utilisant le style concis, ou devrais-je essayer de faire correspondre l'ancien code pour plus de cohérence? La cohérence vaut-elle suffisamment dans ce cas pour justifier l'ancien format?
KChaloux
la source
Voir cette question et réponse .
Peter K.
@PeterK. Informatif. Ne répond pas si je dois ou non m'inquiéter de garder le style du reste du programme, ou s'il s'agit d'un détail suffisamment petit pour ne pas avoir d'importance.
KChaloux
@KChaloux: Compris! C'est pourquoi c'est un commentaire, pas une réponse. :-)
Peter K.
@PeterK. Fair 'nuff = p
KChaloux
1
une autre raison de ne pas changer est si quelque chose est passé sur le fil avec WCF - les propriétés automatiques ont des problèmes de contrat (en raison des caractères non valides dans les noms des champs de support créés par le .NET)
Leon

Réponses:

16

Il y a quelques cas où le style dit "plus ancien" est toujours requis:

R: Types immuables utilisant l'immuabilité fournie par le langage. Le readonlymodificateur en C # gèle cette valeur après la construction. Il n'y a aucun moyen de reproduire cela avec des propriétés implémentées automatiquement (pour l'instant).

public sealed class FooBarBazInator
{
    private readonly double foo;

    public FooBarBazInator(double foo)
    {
        this.foo = foo;
    }

    public double Foo
    {
        get
        {
            return this.foo;
        }
    }
}

B: Vos getters / setters ont une logique quelconque. Le code WPF et Silverlight (et similaire) lié aux données de vos classes sera implémenté INotifyPropertyChangedcomme suit :

public class FooBarBazInator : INotifyPropertyChanged
{
    private double foo;

    public event PropertyChangedEventHandler PropertyChanged;

    public double Foo
    {
        get
        {
            return this.foo;
        }

        set
        {
            this.foo = value;
            this.RaisePropertyChanged("Foo");
        }
    }

    private void RaisePropertyChanged(string propertyName)
    {
        var propertyChanged = this.PropertyChanged;

        if (propertyChanged != null)
        {
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

À part ça, je dirais utiliser le nouveau style. Gardez votre code concis et offre moins de surface pour que les bogues se faufilent. De plus, s'il doit jamais changer pour inclure la logique, il ne sera pas incompatible avec les signatures.

Jesse C. Slicer
la source
2
Tout le monde semble d'accord pour adopter le nouveau style. Vous obtenez un +1 et le crédit de réponse pour m'avoir parlé de la nécessité d'un champ avec le readonlymodificateur, que je ne connaissais pas. = D
KChaloux
3
Certains (dont moi) considèrent les propriétés automobiles avec un accesseur privé comme étant «suffisamment bonnes» pour les propriétés immuables. Certes, ils peuvent ne pas être vraiment immuables, mais vous devez juste faire attention à ne pas utiliser le setter en dehors du constructeur.
svick
2
une autre raison est que si vous voulez passer la valeur comme refne fonctionnant pas avec les propriétés, vous avez donc besoin du champ
stijn
1
Un outil comme Fody peut être implémenté INotifyPropertyChangedsans avoir besoin de champs de support explicites. github.com/Fody/PropertyChanged
Sebastian Redl
3
Prenez note: C # 6 permet des "propriétés auto getter-only" qui fournit en une seule ligne ce que mon exemple "A" fait.
Jesse C. Slicer
6

Je dirais qu'avoir un champ de support explicite est tout simplement inutile: cela rend votre code plus long et plus difficile à lire. Cela ajoute également un potentiel d'erreur:

public double Bar
{
    get { return _baz; }
    set { _bar = value; }
}

Je pense qu'une erreur comme celle-ci pourrait se produire assez facilement et serait assez difficile à repérer.

Et si vous voulez de la cohérence, faites-le dans l'autre sens: changez le code qui utilise les champs de sauvegarde en code qui utilise les propriétés automatiques. C'est particulièrement facile si vous utilisez un outil de refactoring comme ReSharper (et si vous n'utilisez pas quelque chose comme ça, je pense que vous devriez commencer).

Bien sûr, il y a encore des cas où il est nécessaire d'avoir un champ de support, mais je pense que ce n'est pas pertinent pour cette question.

svick
la source
J'ai en fait fait cela sur quelques-uns des anciens champs lors de la refactorisation du code ... c'est pénible.
KChaloux
1

Même si la question est assez ancienne, j'ai pensé à ajouter quelques points que j'ai lus dans un livre qui mérite d'être mentionné ici.

  1. Les moteurs de sérialisation au moment de l'exécution conservent le nom du fichier dans un flux sérialisé. Le nom du champ de sauvegarde pour une propriété implémentée automatiquement (AIP) est déterminé par le compilateur, et il pourrait en fait changer le nom de ce champ de sauvegarde chaque fois que vous recompilez votre code, annulant la possibilité de désérialiser les instances de tout type contenant un AIP. N'utilisez donc pas AIP avec un type que vous souhaitez sérialiser ou désérialiser.

  2. Lors du débogage, vous ne pouvez pas placer de point d'arrêt sur une méthode AIP get ou set, vous ne pouvez donc pas facilement détecter quand une application obtient ou définit cette propriété. Vous pouvez définir des points d'arrêt sur des points d'arrêt implémentés manuellement

RAM
la source
3
# 1 - La plupart des sérialiseurs ignorent les propriétés / champs privés par défaut; Je n'ai jamais rencontré les problèmes que vous mentionnez. # 2 - Ceci est corrigé dans VS2015 et peut être contourné dans VS2013. Consultez cet article de Visual Studio Magazine .
Brian
-3

Y a-t-il une bonne raison de préférer le style plus ancien et plus explicite au nouveau, plus concis?

Pas vraiment. Bien que je soutienne que chaque fois que vous aviez une propriété de passage comme celle-ci, vous auriez dû utiliser un champ public pour commencer.

Dois-je écrire de nouvelles classes en utilisant le style concis, ou devrais-je essayer de faire correspondre l'ancien code pour plus de cohérence? La cohérence vaut-elle suffisamment dans ce cas pour justifier l'ancien format?

En supposant que vous ayez ignoré les conseils ci-dessus et que vous ayez des propriétés d'intercommunication, je ne viserais pas à faire correspondre le style. Idéalement, vous devriez revenir en arrière et refaçonner l'ancien style dans le nouveau style pour être cohérent, mais les chances que les gens interprètent mal ce code soient minces.

Telastyn
la source
@svick - Bah. À moins que vous ne réfléchissiez au type, il n'y a pas de différence. Si vous exposez publiquement le type en tant qu'interface de service ou de bibliothèque, vous allez exposer une interface qui nécessitera de toute façon une propriété. Le PO ne fait rien de tout cela.
Telastyn
2
Il n'y a aucune raison technique dans ces cas limités. N'oubliez pas que les propriétés se comportent différemment dans le débogueur, les diagrammes de classes et l'intellisense, et juste au moment où vous pensez que vous ne faites pas de réflexion, le framework .NET l'est. WinForms, WPF et plusieurs autres composants utilisent en interne la réflexion et traitent les propriétés séparément, donc les conseils d'utilisation des champs publics seront toujours mal reçus par de nombreux développeurs sur ce site.
Kevin McCormick
1
@KevinMcCormick et le framework se comportent parfaitement bien (d'après ce que je comprends) avec les champs. Cela n'a pas vraiment d' importance car il y a maintenant des propriétés automatiques, mais le plus gros problème est de rendre les choses publiques en premier lieu. De loin, beaucoup trop de gens pensent que le simple fait de rendre les propriétés des propriétés les absout en quelque sorte de «ne pas rendre les champs publics».
Telastyn
2
Le Framework les gère différemment: essayez d'utiliser un champ public dans EF POCO, liez-le à un DataGridView, utilisez un PropertyGrid, etc. Je viens de faire une recherche rapide dans mon décompilateur vers Type.GetProperties / GetProperty et le Framework appelle la méthode des centaines de fois, mais pas à GetField. En bout de ligne, ils sont différents, et la recommandation de toujours utiliser des propriétés pour les données publiques est basée en partie sur ce fait. Cela n'empêche pas d'utiliser des champs, mais ce cas doit être justifié. Cependant, votre autre point est certainement vrai, exposer les données publiques avec négligence est toujours une mauvaise idée.
Kevin McCormick