.NET-Eigenschaften - Private Set- oder ReadOnly-Eigenschaft verwenden?

45

In welcher Situation sollte ich ein privates Set für eine Eigenschaft verwenden, anstatt es zu einer ReadOnly-Eigenschaft zu machen? Berücksichtigen Sie die beiden folgenden sehr simplen Beispiele.

Erstes Beispiel:

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

Zweites Beispiel:

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

Sie liefern beide die gleichen Ergebnisse. Ist dies eine Situation, in der es kein Richtig und Falsch gibt und es nur eine Frage der Präferenz ist?

tgxiii
quelle
public string Name { get; protected set; }durch Vererbung.
Samis

Antworten:

42

Es gibt ein paar Gründe, um zu verwenden private set.

1) Wenn Sie überhaupt kein Hintergrundfeld verwenden und eine schreibgeschützte automatische Eigenschaft wünschen:

public string Name { get; private set; }   

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

2) Wenn Sie zusätzliche Arbeit leisten möchten, wenn Sie die Variable in Ihrer Klasse ändern und diese an einem einzigen Ort erfassen möchten:

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

Im Allgemeinen ist es jedoch eine Frage der persönlichen Präferenz. Soweit ich weiß, gibt es keine Leistungsgründe, um eine übereinander zu verwenden.

Adam Lear
quelle
1
Fügen Sie dies einfach hinzu, da die Frage auch ein vb.net-Tag hat. In vb.net müssen Sie jedoch einen Unterstützer angeben, wenn Sie privat für get oder set verwenden. Also in vb.net ist es eigentlich weniger Arbeit, die Eigenschaft schreibgeschützt zu machen, denke ich.
user643192
Das habe ich nie gewusst private set. :-)
Afzaal Ahmad Zeeshan
9
Ein Update für die diese Antwort in 2016. C Lesen # 6.0 hat nur lesbar Auto-Eigenschaften eingeführt, die Ihnen eine Nur - Lese - Eigenschaft ohne Trägerfeld haben lassen: public string Name { get; }. Wenn Sie keine veränderbare Eigenschaft möchten, ist dies jetzt die bevorzugte Syntax.
Alexey
4
Ein sehr guter Grund, dies nicht zu verwenden, private setist, dass es nicht so unveränderlich ist, wie wir es gerne vortäuschen. Wenn Sie eine wirklich unveränderliche Klasse implementieren möchten, ist schreibgeschützt ein Muss.
RubberDuck
Dies kann ein Leistungsgrund sein, den Sie NICHT schreibgeschützt verwenden sollten. Scheint unnötiges Kopieren von Strukturen zu verursachen, wenn auf Methoden eines schreibgeschützten Strukturfelds zugegriffen wird. codeblog.jonskeet.uk/2014/07/16/…
Triynko
28

Verwenden Sie Private Set, wenn Sie möchten, dass Setter nicht von außen zugänglich sind .

Verwenden Sie readonly, wenn Sie die Eigenschaft nur einmal festlegen möchten . Im Konstruktor oder Variableninitialisierer.

TESTEN SIE DIESES:

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
quelle
Obwohl ich Ihrer Antwort zustimme, könnte Ihr Beispiel eine Verbesserung gebrauchen. Möglicherweise möchten Sie einen Kommentar abgeben, wo der Compilerfehler auftreten würde.
Michael Richardson
Hinweis: Die VB.NET-Syntax für das C # readonly-Schlüsselwort gilt ReadOnlyfür das Feld und nicht für die Eigenschaft.
Zev Spitz
8

Darf ich eine dritte Option vorschlagen?

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

Dadurch ist die Name-Eigenschaft für den gesamten externen Code schreibgeschützt und es wird eine explizite Set-Methode bereitgestellt. Ich bevorzuge das explizite Set, anstatt einfach das Set für die Name-Eigenschaft zu verwenden, da Sie den Wert beim Festlegen ändern. Wenn Sie einen Eigenschaftswert festlegen , erwarten Sie normalerweise, dass Sie denselben Wert zurückerhalten , wenn Sie den Befehl get später aufrufen . Dies würde nicht passieren, wenn Sie ToTitleCase im Set ausführen würden .

Wie Sie sagten, gibt es jedoch keine richtige Antwort.

Dave Wise
quelle
Ich glaube, 'private set' hat eine spezielle Semantik im Compiler (und tritt nicht nur als privater Accessor auf). Ist dies auch bei geschützten Sets der Fall? Wenn nicht, wo ist die Semantik der geschützten Menge, wenn die private Menge eine spezielle Semantik hat? Ich konnte keine Dokumentation finden, die dies erklärt.
Sprague,
1
+1, aber ich würde die Methode "Rename" anstelle von "SetName" aufrufen.
MattDavey
4

Verwenden Sie nicht das zweite Beispiel. Der springende Punkt bei der Verwendung einer Eigenschaft ist, den gesamten Zugriff durch diesen Getter und Setter zu leiten, auch wenn nichts anderes als der Getter-Vorgang und die Setter-Einstellung ausgeführt werden ein Platz.

In Ihrem zweiten Beispiel wird dies beim Festlegen der Eigenschaft aufgegeben. Wenn Sie diesen Ansatz in einer großen, komplexen Klasse verwendet haben und später das Verhalten der Eigenschaft ändern müssen, befinden Sie sich im Land der Suche und Ersetzung, anstatt die Änderung an einem Ort vorzunehmen - dem privaten Setter.

Carson63000
quelle
2

Wann immer ich die Zugriffsebene eines Setters ändern musste, habe ich sie im Allgemeinen in "Geschützt" (nur diese Klasse und abgeleitete Klassen können den Wert ändern) oder "Freund" (nur Mitglieder meiner Assembly können den Wert ändern) geändert.

Die Verwendung von "Privat" ist jedoch sinnvoll, wenn Sie neben dem Ändern des Sicherungswerts noch andere Aufgaben im Setter ausführen möchten. Wie bereits erwähnt, empfiehlt es sich, nicht direkt auf Ihre Hintergrundwerte zu verweisen, sondern nur über deren Eigenschaften darauf zuzugreifen. Dadurch wird sichergestellt, dass spätere Änderungen, die Sie an einer Eigenschaft vornehmen, sowohl intern als auch extern angewendet werden. Und es gibt praktisch keine Leistungseinbußen beim Verweisen auf eine Eigenschaft im Vergleich zu ihrer Sicherungsvariablen.

Prlaba
quelle
0

Und es gibt praktisch keine Leistungseinbußen ...

Der Zugriff auf eine Eigenschaft ist jedoch langsamer als der Zugriff auf die Sicherungsvariable. Getter und Setter einer Eigenschaft sind Methoden, die einen Call und einen Return erfordern, während auf die Backing-Variable einer Eigenschaft direkt zugegriffen wird.

Aus diesem Grund wird in Fällen, in denen auf den Getter einer Eigenschaft innerhalb eines Codeblocks häufig zugegriffen werden kann, der Wert der Eigenschaft manchmal zuerst zwischengespeichert (in einer lokalen Variablen gespeichert) und stattdessen die lokale Variable verwendet. Dies setzt natürlich voraus, dass die Eigenschaft nicht asynchron geändert werden kann, während der Block ausgeführt wird.

Prlaba
quelle