117 votes

Comment mettre en œuvre une propriété en lecture seule ?

Je dois mettre en œuvre un en lecture seule de mon type. De plus, la valeur de cette propriété sera définie dans le constructeur et ne sera pas modifiée (j'écris une classe qui expose des commandes d'interface utilisateur personnalisées pour WPF, mais cela n'a pas d'importance).

Je vois deux façons de procéder :

  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 de FxCop disant que je ne devrais pas avoir de variables membres publiques, il semble que la seconde soit la bonne façon de faire. Est-ce correct ?

Y a-t-il une différence entre une propriété en lecture seule et un membre en lecture seule dans ce cas ?

85voto

CodesInChaos Points 60274

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

public object MyProperty { get; }

Ainsi, lorsque vous n'avez pas besoin de prendre en charge les anciens compilateurs, vous pouvez disposer d'une propriété véritablement en lecture seule avec un code tout aussi concis que celui d'un champ en lecture seule.


Versioning :
Je pense que cela ne fait pas une grande différence si vous n'êtes intéressé que 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é dotée d'un setter sans casser 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 suivre la convention. Un cas où cela peut se retourner contre vous est le code basé sur la réflexion. Il peut n'accepter que des propriétés et non des champs, par exemple un éditeur/visualisateur de propriétés.

Sérialisation
Le passage d'un champ à une propriété brisera probablement de nombreux sérialiseurs. Et AFAIK XmlSerializer ne sérialise que les propriétés publiques et non les champs publics.

Utilisation d'une propriété automatique
Une autre variante courante est l'utilisation d'une propriété automatique (autoproperty) avec une propriété privée (private setter). Bien qu'il s'agisse d'une propriété courte, elle n'impose pas la lecture seule. Je préfère donc les autres variantes.

Le champ en lecture seule est auto-documenté
Le champ présente toutefois un avantage :
Il suffit d'un coup d'œil à l'interface publique pour comprendre qu'elle est en fait immuable (à l'exception de la réflexion). Alors que dans le cas d'une propriété, vous pouvez seulement voir que usted ne peut pas la modifier, il faut donc se référer à la documentation ou à l'implémentation.

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

83voto

Oded Points 271275

La deuxième solution est l'option préférée.

private readonly int MyVal = 5;

public int MyProp { get { return MyVal;}  }

Cela permettra de s'assurer 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 façon vous n'exposez pas un membre interne, ce qui vous permet de modifier l'implémentation interne à l'avenir.

61voto

Bob Sammers Points 656

Avec l'introduction de C# 6 (dans VS 2015), vous pouvez maintenant avoir get -uniquement les propriétés automatiques, dans lesquelles le champ d'appui implicite est readonly (c'est-à-dire que des valeurs peuvent être attribué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
}

Il est désormais possible d'initialiser des propriétés (avec ou sans setter) en ligne :

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

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

Malheureusement, cela ne garantit pas l'immutabilité au niveau de l'interface publique (comme dans la remarque de @CodesInChaos sur l'auto-documentation), car pour un consommateur de la classe, l'absence de setter est indiscernable de l'existence d'un setter privé.

27voto

Mekroebo Points 39

En C# 9, Microsoft a introduit une nouvelle façon de définir les propriétés uniquement lors de l'initialisation en utilisant la fonction les init accesseur comme suit :

public class Person
{
  public string FirstName { get; init; }
  public string LastName { get; init; }
}

De cette façon, vous pouvez attribuer des valeurs lors de l'initialisation d'un nouvel objet :

var person = new Person
{
  Firstname = "John",
  LastName = "Doe"
}

Mais plus tard, vous ne pourrez plus le changer :

person.LastName = "Denver"; // throws a compiler error

13voto

rmx Points 2364

Vous pouvez le faire :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X