web-dev-qa-db-fra.com

Comment implémenter une propriété en lecture seule

J'ai besoin d'implémenter une propriété lecture seule sur mon type. De plus, la valeur de cette propriété va être définie dans le constructeur et ne sera pas modifiée (j'écris une classe qui expose les commandes personnalisées de l'interface utilisateur routées pour WPF, mais cela n'a pas d'importance).

Je vois deux façons de le faire:

  1. class MyClass
    {
        public readonly object MyProperty = new object();
    }
    
  2. class MyClass
    {
        private readonly object my_property = new object();
        public object MyProperty { get { return my_property; } }
    }
    

Avec toutes ces erreurs FxCop disant que je ne devrais pas avoir de variables membres publiques, il semble que la seconde soit la bonne façon de le faire. Correct?

Existe-t-il une différence entre une propriété get only et un membre en lecture seule dans ce cas?

J'apprécierais vos commentaires/conseils/etc.

69
akonsu

Versioning:
Je pense que cela ne fait pas beaucoup de différence si vous êtes uniquement intéressé par la compatibilité des sources.
L'utilisation d'une propriété est préférable pour la compatibilité binaire, car vous pouvez la remplacer par une propriété qui a un séparateur sans rompre le code compilé en fonction de votre bibliothèque.

Convention:
Vous suivez la convention. Dans des cas comme celui-ci, où les différences entre les deux possibilités sont relativement mineures, il est préférable de respecter la convention. Un cas où il pourrait revenir vous mordre est un code basé sur la réflexion. Il peut accepter uniquement des propriétés et non des champs, par exemple un éditeur/visualiseur de propriétés.

Sérialisation
Le changement de champ en propriété endommagera probablement beaucoup de sérialiseurs. Et AFAIK XmlSerializer ne sérialise que les propriétés publiques et non les champs publics.

tilisation d'une propriété automatique
Une autre variante courante consiste à utiliser une propriété automatique avec un passeur privé. Bien que ce soit court et une propriété, il n’impose pas la lecture seulement. Donc je préfère les autres.

Le champ en lecture seule est une auto-documentation
Il existe cependant un avantage du champ:
Il est clair en un coup d’œil sur l’interface publique qu’elle est en fait immuable (sauf réflexion). Alors que dans le cas d'une propriété, vous ne pouvez que constater que vous ne pouvez pas le changer, vous devez donc vous référer à la documentation ou à la mise en oeuvre.

Mais pour être honnête, j'utilise le premier assez souvent dans le code de l'application car je suis paresseux. Dans les bibliothèques, je suis généralement plus rigoureux et respecte la convention.

C # 6.0 ajoute les propriétés automatiques en lecture seule

public object MyProperty { get; }

Ainsi, lorsque vous n'avez pas besoin de prendre en charge des compilateurs plus anciens, vous pouvez avoir une propriété vraiment en lecture seule avec un code aussi concis qu'un champ en lecture seule.

48
CodesInChaos

La deuxième façon est l'option privilégiée.

private readonly int MyVal = 5;

public int MyProp { get { return MyVal;}  }

Cela garantira que MyVal ne peut être attribué qu'à l'initialisation (il peut également être défini dans un constructeur).

Comme vous l'avez noté - de cette manière, vous n'exposez pas un membre interne, ce qui vous permet de modifier la mise en œuvre interne à l'avenir.

58
Oded

Avec l’introduction de C # 6 (dans VS 2015), vous pouvez désormais avoir des propriétés automatiques get uniquement, dans lesquelles le champ de sauvegarde implicite est readonly (les valeurs peuvent être affectées dans le constructeur mais pas ailleurs):

public string Name { get; }

public Customer(string name)  // Constructor
{
    Name = name;
}

private void SomeFunction()
{
    Name = "Something Else";  // Compile-time error
}

Et vous pouvez maintenant également initialiser les propriétés (avec ou sans un setter) en ligne:

public string Name { get; } = "Boris";

En revenant à la question, cela vous donne les avantages de l'option 2 (membre public est une propriété, pas un champ) avec la brièveté de l'option 1.

Malheureusement, cela ne garantit pas l'immuabilité au niveau de l'interface publique (comme dans l'argument de @ CodesInChaos à propos de l'auto-documentation), car pour un consommateur de la classe, ne pas définir de paramètre est impossible à distinguer d'un paramètre privé.

41
Bob Sammers

Tu peux le faire:

public int Property { get { ... } private set { ... } }
11
Nobody

Je conviens que la deuxième voie est préférable. La seule raison réelle de cette préférence est la préférence générale que les classes .NET ne disposent pas de champs publics. Cependant, si ce champ est en lecture seule, je ne vois pas comment il pourrait y avoir de réelles objections autres qu'un manque de cohérence avec d'autres propriétés. La vraie différence entre un champ readonly et une propriété get-only est que le champ readonly fournit une garantie que sa valeur ne changera pas pendant la durée de vie de l'objet et qu'une propriété get-only ne le fera pas.

5
Eric Mickelsen

La deuxième méthode est préférée en raison de l’encapsulation. Le champ readonly peut certainement être public, mais cela va à l’encontre des idiomes C # dans lesquels vous avez accès aux données via des propriétés et non des champs.

Le raisonnement derrière cela est que la propriété définit une interface publique et si l'implémentation de sauvegarde de cette propriété change, vous ne finissez pas par casser le reste du code car l'implémentation est cachée derrière une interface.

4
Joshua Rodgers

encore une autre façon (mon préféré), en commençant par C # 6

private readonly int MyVal = 5;

public int MyProp => MyVal;

https://docs.Microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties#expression-body-definitions

0
owns