Lorsque vous définissez une valeur sur une variable à l'intérieur d'une classe la plupart du temps, nous avons deux options:
private string myValue;
public string MyValue
{
get { return myValue; }
set { myValue = value; }
}
Existe-t-il une convention qui détermine comment attribuer des valeurs aux variables à l'intérieur de nos classes? Par exemple, si j'ai une méthode à l'intérieur de la même classe, dois-je l'affecter en utilisant la propriété ou en utilisant la variable privée. Je l'ai vu fonctionner dans les deux sens, donc je me demandais si c'était un choix ou si la performance était un facteur (mineur, probablement).
Je voudrais aller un peu plus loin et le porter à 3 cas. Bien qu'il existe des variations sur chacun, ce sont les règles que j'utilise la majorité du temps lors de la programmation C #.
Dans les cas 2 et 3, accédez toujours à l'accesseur de propriété (et non à la variable de champ). Et dans le cas 1, vous n'avez même pas à 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 préfère cela à un setter privé, car un setter privé ne garantit pas l'immuabilité.
public class Abc
{
private readonly int foo;
public Abc(int fooToUse){
foo = fooToUse;
}
public int Foo { get{ return foo; } }
}
2.) Variable POCO. Une variable simple qui peut obtenir/définir à n'importe quelle portée publique/privée. Dans ce cas, j'utiliserais simplement une propriété automatique.
public class Abc
{
public int Foo {get; set;}
}
3.) Propriétés de liaison de 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"); }
}
}
Généralement, 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 insérera un simple get ou set pour vous et le code MSIL final sera probablement identique.
Dépend.
Vous devez d'abord préférer les propriétés automatiques lorsque cela est possible:
public string MyValue {get;set;}
Deuxièmement, la meilleure approche serait probablement d'utiliser les propriétés, si vous avez une logique, vous devriez probablement la passer vous-même, surtout si cette logique est la synchronisation des threads.
Mais vous devez également tenir compte du fait que cela peut nuire à vos performances (un peu), si vous ne synchronisez pas correctement, vous pouvez vous bloquer vous-même, et parfois le bon chemin consiste à contourner la logique de la propriété.
Eh bien, l'approche directe serait de simplement l'assigner à la variable elle-même, puisque vous êtes dans une méthode de classe et que vous contrôlez le comportement de la classe, de toute façon.
Mais tout l'intérêt des propriétés est qu'elles font abstraction de la variable. Alors qu'une propriété aussi simple que dans votre exemple n'a absolument 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 voulez que ces choses se fassent automatiquement lorsque vous modifiez la propriété à l'intérieur de la classe, il est bien sûr plus propre de travailler sur la propriété au lieu de la variable pour ne pas avoir à modifier chaque affectation de variable lorsque le comportement de définition de la propriété change.
Il suffit de raisonner conceptuellement. La propriété est en fait une poignée pour accéder à un état interne de l'objet, qui peut être composé de plusieurs variables membres. Vous devez donc vous demander si vous souhaitez modifier uniquement l'état interne sous-jacent (ou seulement une partie de celui-ci) ou la propriété abstraite représentant cet état dans son ensemble, et la plupart du temps, c'est bien ce dernier puisque vous souhaitez généralement que votre objet ait toujours un état cohérent.
S'il y a une chance que l'implémentation get/set de cette propriété change parfois plus tard (par exemple, vous voulez déclencher un événement lors de l'appel de set
, ou vous ajouterez un mécanisme d'évaluation paresseux plus tard à votre get
function), alors il peut être une bonne idée que votre code à l'intérieur de la classe utilise la propriété dans presque tous les cas, sauf dans les cas - très probablement rares - où vous explicitement ne pas voulez ceux des mécanismes d'évaluation des événements ou paresseux à utiliser.
Quoi qu'il en soit, quoi que vous fassiez, il y a de fortes chances que lorsque vous modifiez l'implémentation de propriété plus tard de cette manière, vous devrez regarder tous les endroits à l'intérieur de votre classe accédant à cette propriété pour vérifier si la propriété est réellement accessible ou si une variable privée doit être utilisée.
J'utilise toujours la propriété publique.
Souvent, une logique qui doit toujours s'exécuter lorsque la propriété est définie est ajoutée à la méthode set
d'une propriété, et en définissant le champ privé à la place, le setter public contourne toute logique qui s'y trouve.
Vous avez un commentaire à propos de MVVM menant à cette question, et je pense que cela est encore plus important lorsque vous travaillez avec MVVM. De nombreux objets envoient une notification PropertyChange
au setter, 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 s'exécuteront jamais à moins que vous ne déclenchiez également manuellement l'événement PropertyChanged
.
Généralement, c'est à vous de décider ce que vous devez faire avec une propriété et son champ de sauvegarde lors de l'obtention/du paramétrage.
Le plus souvent, pour être cohérent dans le code, 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 faisant ce réglage doit être supprimée de la classe et placée ailleurs 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 où que la classe elle-même fasse 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 principale situation à laquelle je peux penser lorsque vous devez utiliser le champ de support et NON l'accesseur de propriété est lorsque 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 en est un exemple; vous pouvez avoir une classe qui utilise deux valeurs de propriété pour 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.
Utilisez toujours 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 à travers laquelle vous pouvez accéder en toute sécurité à un modèle de données. Pour la plupart des situations, vous souhaitez toujours accéder en toute sécurité au modèle de données via cette interface, comme:
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 logique d'utiliser la forme radians de SomeAngle
, alors utilisez-la par tous les moyens.
À la fin, assurez-vous de boire votre propre kool-aid. Votre API publique doit être suffisamment résistante pour fonctionner en interne.