Variable privée vs propriété?


41

Lorsque vous définissez une valeur sur une variable à l'intérieur d'une classe, la plupart du temps, deux options sont présentées:

private string myValue;
public string MyValue
{
   get { return myValue; }
   set { myValue = value; }
}

Existe-t-il une convention qui détermine la manière dont nous devrions attribuer des valeurs aux variables à l'intérieur de nos classes? Par exemple, si j'ai une méthode dans la même classe, dois-je l'affecter en utilisant la propriété ou en utilisant la variable privée. Je l'ai vu faire les deux choses, alors je me demandais si c'était un choix ou si la performance était un facteur (mineur, probablement).

Réponses:


23

Je voudrais aller un peu plus loin et l'amener à 3 cas. Bien qu'il y ait des variations sur chacun, ce sont les règles que j'utilise la plupart du temps lors de la programmation en C #.

Dans les cas 2 et 3, accédez toujours à l'accesseur de propriété (pas à la variable de champ). Et dans le cas 1, vous n’êtes même plus obligé de faire ce choix.

1.) Propriété immuable (transmise au constructeur ou créée au moment de la construction). Dans ce cas, j'utilise une variable de champ, avec une propriété en lecture seule. Je choisis ceci par rapport à un poseur privé, puisqu'un poseur privé ne garantit pas l'immutabilité.

public class Abc
{ 
  private readonly int foo;

  public Abc(int fooToUse){
    foo = fooToUse;
  }

  public int Foo { get{ return foo; } }
}

2.) variable POCO. Une simple variable pouvant obtenir / définir n'importe quelle portée publique / privée. Dans ce cas, je voudrais simplement utiliser une propriété automatique.

public class Abc
{ 
  public int Foo {get; set;}
}

3.) Propriétés de liaison ViewModel. Pour les classes qui prennent en charge INotifyPropertyChanged, je pense que vous avez besoin d'une variable de champ de sauvegarde privée.

public class Abc : INotifyPropertyChanged
{
  private int foo;

  public int Foo
  {
    get { return foo; }
    set { foo = value;  OnPropertyChanged("foo"); }
  }
}

2
+1 pour l'exemple MVVM. En fait, c'est ce qui a déclenché la question.
Edward

4
+1: Mélangez 2/3 avec AOP et vous avez une excellente façon d'utiliser INPC. [Notifie] public int Foo {get; ensemble; }
Steven Evers

1
@Job Pour toute classe accédant à la classe, un séparateur privé suffit pour l'immutabilité. Toutefois, à l'intérieur de la classe, le séparateur privé n'empêche pas les réglages répétés de la valeur après la construction initiale. Une fonctionnalité de langage telle qu'un «ensemble privé en lecture seule» pourrait contourner ce problème de manière conceptuelle, mais il n'existe pas.
Sheldon Warkentin

1
Je suis nouveau en C #, alors dites-moi, pourquoi utiliser public int Foo {get; set;}au lieu de public int Foo?

1
Si une classe ou une structure se comporte comme un POCO ou un PODS, quel avantage réel y-a-t-il à encapsuler des champs dans des propriétés? Je comprends parfaitement que le fait d'encapsuler des champs dans des propriétés est utile lorsqu'une classe ou une structure a besoin, ou pourrait avoir besoin à l'avenir, de conserver des invariants par rapport à son contenu (peut-être en s'assurant que les autres objets sont mis à jour afin de les faire correspondre), mais si une classe ou une structure spécifie que les consommateurs peuvent écrire toutes les valeurs dans n'importe quel ordre sans restrictions ni effets secondaires, quels comportements utiles pourraient éventuellement être ajoutés à un accesseur membre?
Supercat

18

En règle générale, je dirais assigner au champ dans le constructeur et utiliser la propriété partout ailleurs. De cette façon, si quelqu'un ajoute des fonctionnalités à la propriété, vous ne la manquerez nulle part.

Ce n'est certainement pas un facteur de performance. L’optimiseur incorporera un simple get ou set pour vous et le code final MSIL sera probablement identique.


Une raison particulière pour utiliser le champ dans le constructeur? Moins de chance d'effets secondaires étranges?
Signer le

4
@Sign: Je suppose que s'il existe une validation sur la propriété (maintenant ou dans le futur), vous ne voulez pas courir le risque que la validation échoue pendant la construction. La validation n’est pas logique à ce stade car la stabilité de l’objet ne peut pas être garantie tant que le constructeur n’a pas terminé.
Steven Evers

@Sign: Ce que tu as dit et ce que Snorfus a dit. Ou si je souhaite consigner les modifications apportées à une propriété, je ne souhaite probablement pas consigner le paramètre initial. Mais j'ai bien dit "en général".
pdr

3
@Sign: le problème est le suivant: si la méthode set de la propriété peut être substituée dans une sous-classe, vous pouvez avoir un effet secondaire lors de la création de l'objet ou un objet incohérent (c'est-à-dire que la propriété remplacée est programmée pour ne définir aucune valeur pour. ce domaine). L'utilisation de propriétés dans les constructeurs n'est sûre que si la méthode set est privée ou si la classe est scellée.
Diego

4

Dépend.

Tout d'abord, vous devriez préférer les propriétés automatiques lorsque cela est possible:

public string MyValue {get;set;}

Deuxièmement, la meilleure approche consisterait probablement à utiliser les propriétés. Si vous avez une logique, vous devriez probablement la traverser vous-même, en particulier si cette logique est la synchronisation des threads.

Cependant, vous devez également prendre en compte le fait que cela peut gêner vos performances (un peu), si vous synchronisez incorrectement, vous pouvez vous bloquer, et parfois le chemin correct consiste à contourner la logique de la propriété.


3
J'aime aussi public string MyValue {get; private set;}.
Job le

3

Eh bien, l'approche directe au point serait simplement de l'attribuer à la variable elle-même, puisque vous êtes dans la méthode d'une classe et que vous contrôlez le comportement de la classe, de toute façon.

Mais tout ce qui concerne les propriétés, c'est qu'elles font abstraction de la variable. Alors qu'une propriété aussi simple que dans votre exemple n'a aucune utilité sur une simple variable membre publique, les propriétés font (ou devraient faire) des choses supplémentaires à l'intérieur de leurs getters et setters. Et si vous souhaitez que ces opérations soient effectuées automatiquement lorsque vous modifiez la propriété dans la classe, il est bien sûr préférable de travailler sur la propriété plutôt que sur la variable pour ne pas avoir à modifier chaque affectation de variable lorsque le comportement du paramètre de la propriété change.

Il suffit de raisonner conceptuellement. La propriété est en réalité un handle permettant d'accéder à un état interne de l'objet, qui peut être composé de plusieurs variables membres. Vous devez donc vous demander si vous voulez changer l'état interne sous-jacent seulement (ou seulement une partie de celui-ci) ou la propriété abstraite représentant cet état dans son ensemble, et la plupart du temps, il s'agit bien de ce dernier, car vous voulez généralement que votre objet ait toujours un état cohérent.


2

S'il existe un risque que l'implémentation get / set de la propriété change parfois ultérieurement (par exemple, si vous souhaitez déclencher un événement lors de l'appel set, ou si vous ajouterez un mécanisme d'évaluation paresseux ultérieurement à votre getfonction), cela peut être une bonne idée que votre code dans la classe utilisera la propriété dans presque tous les cas, sauf dans les cas - très probablement rares - dans lesquels vous ne souhaitez pas explicitement utiliser ces mécanismes d'événement ou d'évaluation paresseux.

Quoi qu'il en soit, quoi que vous fassiez, il y a de fortes chances que, lorsque vous modifierez la mise en œuvre de la propriété de cette manière, vous devrez examiner tous les lieux de votre classe accédant à cette propriété pour vérifier si la propriété sera réellement accessible ou si variable privée doit être utilisé.


2

J'utilise toujours la propriété publique.

Souvent, une logique qui doit toujours être exécutée lorsque la propriété est définie est ajoutée à la setméthode d'une propriété, et définir le champ privé à la place du paramètre public contournera toute logique.

Vous avez un commentaire à propos de MVVM qui a conduit à cette question, et j'estime que c'est encore plus important lorsque vous travaillez avec MVVM. De nombreux objets émettent une PropertyChangenotification au créateur, et d'autres objets peuvent s'abonner à cet événement pour exécuter une action lorsque des propriétés spécifiques changent. Si vous définissez la variable privée, ces actions ne seront jamais exécutées à moins que vous ne souleviez également l' PropertyChangedévénement manuellement .


+1 Oui dans la plupart des cas (MVVM), l'événement PropertyChanged est indispensable. Et cela ne peut être renvoyé que dans une propriété. Bonne explication.
Edward

1

Généralement, c'est à vous de décider de ce que vous devez faire avec une propriété et son champ de sauvegarde lorsque vous obtenez / définissez.

Le plus souvent, pour que le code soit cohérent, vous devez utiliser des accesseurs publics partout où ils sont disponibles et appropriés. Cela vous permet de refactoriser avec un minimum de changement de code; si la méthode utilisant ce paramètre doit être supprimée de la classe et placée ailleurs, là où le champ de support n'est plus disponible (comme une classe de base), qui s'en soucie? Vous utilisez quelque chose qui est disponible partout où la classe elle-même doit faire le travail. Le champ de support, dans la plupart des cas, est un détail d'implémentation; personne en dehors de votre classe ne devrait savoir qu'il existe.

La situation principale à laquelle je peux penser quand vous devez utiliser le champ de sauvegarde et NON PAS l'accesseur de propriété est quand l'accesseur a une logique supplémentaire (validation ou mise à jour d'autres informations d'état dans la classe) que vous ne voulez pas exécuter. La population initiale d'un objet est un exemple; vous pouvez avoir une classe qui utilise deux valeurs de propriété pour en calculer une troisième, qui est également stockée dans un champ de sauvegarde (pour des raisons de persistance). Lors de l'initialisation d'une nouvelle copie de cet objet à partir des données de la base de données, les accesseurs de propriété qui recalculent chacun la troisième valeur peuvent se plaindre si l'autre valeur nécessaire n'est pas définie. En utilisant les champs de support pour définir les valeurs initiales de ces deux (ou trois) propriétés, vous contournez la logique de validation / calcul jusqu'à ce que l'instance soit dans un état suffisamment cohérent pour que la logique fonctionne normalement.


0

Toujours utiliser celui qui a du sens. Oui, je sais que cela semble assez faux au point d’être une non-réponse.

Le point de propriétés est de fournir une interface par laquelle vous pouvez accéder en toute sécurité à un modèle de données. Dans la plupart des situations, vous souhaitez toujours accéder en toute sécurité au modèle de données via cette interface, par exemple:

public Foo Bar
{
  get { return _bar; }
  set { _bar = doSomethingTo(value); }
}

Mais dans d'autres situations, vous pouvez simplement utiliser une propriété comme vue d'un modèle de données:

public Double SomeAngleDegrees
{
  get { return SomeAngleRadians * 180 / PI; }
  set { SomeAngleRadians = value * PI / 180; }
}

S'il est judicieux d'utiliser la forme radians de SomeAngle, alors utilisez-la.

À la fin, assurez-vous de boire votre propre kool-aid. Votre API faisant face au public doit être suffisamment résiliente pour fonctionner en interne.

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.