Propriétés .NET - Utiliser un ensemble privé ou une propriété en lecture seule?

45

Dans quelle situation dois-je utiliser un ensemble privé sur une propriété plutôt que d'en faire une propriété en lecture seule? Prenez en considération les deux exemples très simplistes ci-dessous.

Premier exemple:

Public Class Person

    Private _name As String

    Public Property Name As String
        Get
            Return _name
        End Get
        Private Set(ByVal value As String)
            _name = value
        End Set
    End Property

    Public Sub WorkOnName()

        Dim txtInfo As TextInfo = _
            Threading.Thread.CurrentThread.CurrentCulture.TextInfo

        Me.Name = txtInfo.ToTitleCase(Me.Name)

    End Sub

End Class

// ----------

public class Person
{
    private string _name;
    public string Name
    {
        get { return _name; }
        private set { _name = value; }
    }

    public void WorkOnName()
    {
        TextInfo txtInfo = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo;
        this.Name = txtInfo.ToTitleCase(this.Name);
    }
}

Deuxième exemple:

Public Class AnotherPerson

    Private _name As String

    Public ReadOnly Property Name As String
        Get
            Return _name
        End Get
    End Property

    Public Sub WorkOnName()

        Dim txtInfo As TextInfo = _
            Threading.Thread.CurrentThread.CurrentCulture.TextInfo

        _name = txtInfo.ToTitleCase(_name)

    End Sub

End Class

// ---------------

public class AnotherPerson
{
    private string _name;
    public string Name
    {
        get { return _name; }
    }

    public void WorkOnName()
    {
        TextInfo txtInfo = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo;
        _name = txtInfo.ToTitleCase(_name);
    }
}

Les deux donnent les mêmes résultats. Est-ce une situation où il n'y a pas de juste ou de faux, et c'est juste une question de préférence?

tgxiii
la source
public string Name { get; protected set; }par héritage.
samis

Réponses:

42

Il y a plusieurs raisons d'utiliser private set.

1) Si vous n'utilisez pas de champ de sauvegarde et souhaitez une propriété automatique en lecture seule:

public string Name { get; private set; }   

public void WorkOnName()
{
    TextInfo txtInfo = Thread.CurrentThread.CurrentCulture.TextInfo;
    Name = txtInfo.ToTitleCase(Name);
}  

2) Si vous souhaitez effectuer un travail supplémentaire lorsque vous modifiez la variable dans votre classe et que vous souhaitez le capturer à un emplacement unique:

private string _name = string.Empty;
public string Name 
{ 
    get { return _name; }
    private set 
    {
        TextInfo txtInfo = Thread.CurrentThread.CurrentCulture.TextInfo;
        _name = txtInfo.ToTitleCase(value);
    }
}

En général, cependant, c'est une question de préférence personnelle. Autant que je sache, il n'y a aucune raison de performance pour utiliser l'un sur l'autre.

Adam Lear
la source
1
Ajoutez simplement ceci car la question a également une balise vb.net, mais dans vb.net vous devez spécifier un contributeur si vous utilisez private sur get ou set. Donc, dans vb.net, c’est en fait moins de travail de rendre la propriété en lecture seule, je pense.
user643192
Je n'ai jamais su à ce sujet private set. :-)
Afzaal Ahmad Zeeshan
10
Une mise à jour pour ceux qui lisent cette réponse en 2016. C # 6.0 a introduit des propriétés d'auto-lecture seule, qui vous permettent d'avoir une propriété sans un champ readonly de support: public string Name { get; }. Si vous ne voulez pas de propriété mutable, c'est la syntaxe préférée à présent.
Alexey
4
Une très bonne raison de ne pas utiliser private setest que ce n’est pas aussi immuable que nous aimons le prétendre. Si vous voulez implémenter une classe vraiment immuable, la lecture seule est un must.
RubberDuck
Peut être une raison de performance de ne pas utiliser en lecture seule. Semble provoquer une copie inutile des structures lors de l'accès aux méthodes d'un champ struct en lecture seule. codeblog.jonskeet.fr/2014/07/16/…
Triynko Le
28

Utilisez set privé lorsque vous voulez que le setter ne soit pas accessible de l'extérieur .

Utilisez readonly lorsque vous souhaitez définir la propriété une seule fois . Dans le constructeur ou l'initialiseur de variable.

TESTEZ CELA:

void Main()
{
    Configuration config = new Configuration();
    config.ResetConfiguration();

    ConfigurationReadOnly configRO = new ConfigurationReadOnly();
    configRO.ResetConfiguration();
}

public class Configuration
{
    public Color BackgroundColor { get; private set; }
    public Color ForegroundColor { get; private set; }
    public String Text { get; private set; }

    public Configuration()
    {
        BackgroundColor = Color.Black;
        ForegroundColor = Color.White;
        Text = String.Empty;
    }

    public void ResetConfiguration()
    {
        BackgroundColor = Color.Black;
        ForegroundColor = Color.White;
        Text = String.Empty;
    }
}

public class ConfigurationReadOnly
{
    public readonly Color BackgroundColor;
    public readonly Color ForegroundColor;
    public readonly String Text;

    public ConfigurationReadOnly()
    {
        BackgroundColor = Color.Black;
        ForegroundColor = Color.White;
        Text = String.Empty;
    }

    public void ResetConfiguration()
    {
        BackgroundColor = Color.Black; // compile error: due to readonly keyword
        ForegroundColor = Color.White; // compile error: due to readonly keyword
        Text = String.Empty; // compile error: due to readonly keyword
    }
}
asakura89
la source
Bien que je sois d’accord avec votre réponse, votre exemple pourrait être amélioré. Vous voudrez peut-être faire un commentaire là où l'erreur du compilateur se produirait.
Michael Richardson
NB: La syntaxe VB.NET correspondant au readonlymot clé C # est à appliquer ReadOnlyau champ plutôt qu'à la propriété.
Zev Spitz le
8

Puis-je suggérer une troisième option?

public class Person
{
    public string Name { get; protected set; }

    public void SetName(string name)
    {
        TextInfo txtInfo = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo;
        this.Name = txtInfo.ToTitleCase(name);
    }
}

Cela rend la propriété Name effectivement en lecture seule à tout le code extérieur et fournit une méthode Set explicite. Je préfère l'ensemble explicite plutôt que d'utiliser simplement l' ensemble de la propriété Name car vous modifiez la valeur lors de sa définition. Normalement, si vous définissez une valeur de propriété, vous vous attendez à obtenir la même valeur lorsque vous appelez l' option get ultérieurement, ce qui ne se produirait pas si vous définissiez votre ToTitleCase dans l' ensemble .

Cependant, comme vous l'avez dit, il n'y a pas une seule bonne réponse.

Dave Wise
la source
Je pense que 'private set' a une sémantique spéciale dans le compilateur (ne se comporte pas simplement comme un accesseur privé). Est-ce aussi le cas avec un ensemble protégé? Sinon, où est l'équivalent sémantique de l'ensemble protégé si l'ensemble privé a une sémantique spéciale? Je n'ai pas été en mesure de trouver de documentation expliquant cela.
Sprague
1
+1 mais j'appellerais la méthode "Rename" au lieu de "SetName".
MattDavey
4

N'utilisez pas le deuxième exemple. L'utilisation d'une propriété - même s'il ne se passe rien d'autre que le getter et le paramètre du setter - est de canaliser tous les accès via ce getter et ce setter afin que, si jamais vous deviez changer de comportement, tout se trouve une place.

Votre deuxième exemple l'abandonne dans le cas de la définition de la propriété. Si vous utilisiez cette approche dans une classe nombreuse et complexe et que vous deviez par la suite modifier le comportement de la propriété, vous vous retrouveriez dans une zone de recherche et de remplacement, au lieu de procéder à la modification à un endroit: le paramètre privé.

Carson63000
la source
2

Chaque fois que j'ai eu besoin de changer le niveau d'accès d'un setter, je l'ai généralement changé en Protected (seulement cette classe et les classes dérivées peuvent changer la valeur) ou Friend (seuls les membres de mon assembly peuvent changer la valeur).

Mais l'utilisation de Private est parfaitement logique lorsque vous souhaitez effectuer d'autres tâches dans le setter, en plus de modifier la valeur de sauvegarde. Comme indiqué précédemment, il est judicieux de ne pas référencer directement vos valeurs de support, mais uniquement d'y accéder via leurs propriétés. Cela garantit que les modifications que vous apportez ultérieurement à une propriété sont appliquées à la fois en interne et en externe. Et il n'y a pratiquement aucune pénalité de performance à référencer une propriété par rapport à sa variable de sauvegarde.

Prlaba
la source
0

Et il n'y a pratiquement aucune pénalité de performance ...

Mais pour clarifier, accéder à une propriété est plus lent que d'accéder à sa variable de sauvegarde. Le getter et le setter d'une propriété sont des méthodes qui nécessitent un appel et un retour, alors que la variable de sauvegarde d'une propriété est accessible directement.

C'est pourquoi, dans les cas où le getter d'une propriété peut être consulté plusieurs fois dans un bloc de code, la valeur de la propriété est parfois mise en cache en premier (enregistrée dans une variable locale) et la variable locale utilisée à la place. Bien entendu, cela suppose que la propriété ne peut pas être modifiée de manière asynchrone pendant l'exécution du bloc.

Prlaba
la source