web-dev-qa-db-fra.com

Propriétés .NET - Utiliser la propriété Private Set ou ReadOnly?

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);
    }
}

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

48
tgxiii

Il y a plusieurs raisons d'utiliser private set.

1) Si vous n'utilisez aucun 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 voulez faire un travail supplémentaire lorsque vous modifiez la variable à l'intérieur de votre classe et que vous souhaitez capturer cela dans un seul emplacement:

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 à utiliser l'un sur l'autre.

43
Adam Lear

Utilisez set privé quand vous voulez setter n'est pas accessible de l'extérieur.

Utilisez en lecture seule lorsque vous voulez ne définissez la propriété qu'une seule fois. Dans le constructeur ou l'initialiseur de variables.

TESTEZ CECI:

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
    }
}
31
asakura89

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 pour tout le code extérieur et fournit une méthode Set explicite. Je préfère l'ensemble explicite plutôt que de simplement utiliser set sur la propriété Name car vous modifiez la valeur lors de sa définition. Normalement, si vous set une valeur de propriété, vous vous attendez à récupérer la même valeur lorsque vous appellerez get plus tard, ce qui ne se produirait pas si vous faisiez votre ToTitleCase dans le - ensemble.

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

9
Dave Wise

À partir de C # 6.0, des propriétés automatiques getter uniquement ont été ajoutées au langage. Voir ici: https://github.com/dotnet/roslyn/wiki/New-Language-Features-in-C%23-6#getter-only-auto-properties .

Voici un exemple:

public class SomeClass
{
    public int GetOnlyInt { get; }

    public int GetOnlyIntWithInitializer { get; } = 25;

    public SomeClass(int getOnlyInt)
    {
        GetOnlyInt = getOnlyInt;
    }
}
5
Daniel Neel

N'utilisez pas le deuxième exemple. L'intérêt de l'utilisation d'une propriété - même s'il n'y a rien d'autre que le getter et le paramètre setter - est de canaliser tous les accès via ce getter et ce setter de sorte que si vous avez besoin de changer de comportement à l'avenir, tout est dans une place.

Votre deuxième exemple abandonne cela dans le cas de la définition de la propriété. Si vous avez utilisé cette approche dans une grande classe complexe et que vous deviez plus tard changer le comportement de la propriété, vous seriez en recherche et remplacement de terrain, au lieu de faire le changement en un seul endroit - le setter privé.

4
Carson63000

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 (seules 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 plutôt d'y accéder uniquement via leurs propriétés. Cela garantit que les modifications ultérieures que vous apportez à une propriété sont appliquées en interne comme en externe. Et il n'y a pratiquement aucune pénalité de performance pour référencer une propriété par rapport à sa variable de support.

2
Prlaba

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 support. Le getter et le setter d'une propriété sont des méthodes qui nécessitent un appel et un retour, tandis que la variable de support d'une propriété est accessible directement.

C'est pourquoi, dans les cas où le getter d'une propriété est accessible 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 sûr, cela suppose que la propriété ne peut pas être modifiée de manière asynchrone pendant l'exécution du bloc.

0
Prlaba