Gibt es Gründe, private Eigenschaften in C # zu verwenden?


245

Ich habe gerade festgestellt, dass das C # -Eigenschaftskonstrukt auch mit einem privaten Zugriffsmodifikator verwendet werden kann:

private string Password { get; set; }

Obwohl dies technisch interessant ist, kann ich mir nicht vorstellen, wann ich es verwenden würde, da ein privates Feld noch weniger Zeremonien beinhaltet :

private string _password;

und ich kann mir nicht vorstellen , wenn ich jemals fähig sein müssen , intern zu bekommen , aber nicht eingestellt oder Satz aber nicht bekommen ein eigenes Feld:

private string Password { get; }

oder

private string Password { set; }

aber vielleicht gibt es einen Anwendungsfall mit verschachtelten / geerbten Klassen oder vielleicht, wo ein get / set Logik enthalten könnte, anstatt nur den Wert der Eigenschaft zurückzugeben, obwohl ich dazu neigen würde, Eigenschaften streng einfach zu halten und explizite Methoden jede Logik ausführen zu lassen, zB GetEncodedPassword().

Verwendet jemand aus irgendeinem Grund private Eigenschaften in C # oder ist dies nur eines dieser technisch möglichen, aber selten im tatsächlichen Code verwendeten Konstrukte?

Nachtrag

Nette Antworten, durchlesen Ich habe diese Verwendungen für private Immobilien ausgesucht:

  • wenn private Felder träge geladen werden müssen
  • wenn private Felder zusätzliche Logik benötigen oder berechnete Werte sind
  • da private Felder schwierig zu debuggen sein können
  • um "sich selbst einen Vertrag vorzulegen"
  • um eine exponierte Eigenschaft im Rahmen der Serialisierung intern zu konvertieren / zu vereinfachen
  • Umschließen globaler Variablen, die in Ihrer Klasse verwendet werden sollen

Die Technik, die von privaten Immobilien gefördert wird, ist die Selbstkapselung - siehe: sourcemaking.com/refactoring/self-encapsulate-field
LBushkin

Antworten:


212

Ich benutze sie, wenn ich einen Wert zwischenspeichern muss und ihn faul laden möchte.

private string _password;
private string Password
{
    get
    {
        if (_password == null)
        {
            _password = CallExpensiveOperation();
        }

        return _password;
    }
}

42
ein schönes gemeinsames Muster dafür istreturn _password ?? (_password = CallExpensiveOperation());
Marc

1
@EvAlex Lazy <T> ist möglicherweise vorzuziehen, wenn Sie es verwenden können. Es können jedoch nur statische Elemente verwendet werden, sodass Sie nicht auf (nicht statische) Methoden oder andere Mitglieder zugreifen können. Übrigens bevorzuge ich seit einiger Zeit die einzeilige Syntax return _password ?? (_password = CallExpensiveOperation());. Oder Resharper bevorzugt es tatsächlich :).
Bart

4
@Marc seit C # 6 musst du nicht einmal ein get and return schreiben. private string Password => _password ?? (_password = CallExpensiveOperation());
Otto Abnormalverbraucher

1
Mit C # 8 ist es noch kürzer:private string Password => _password ??= CallExpensiveOperation();
Dave M

2
@Bas Das ist kein verzögertes Laden, da CallExpensiveOperation();es während der Erstellung / Initialisierung des enthaltenen Objekts aufgerufen wird und nicht beim ersten Zugriff auf die Eigenschaft.
Stefan Podskubka

142

Die Hauptverwendung in meinem Code ist die verzögerte Initialisierung, wie andere bereits erwähnt haben.

Ein weiterer Grund für private Eigenschaften über Felder ist, dass private Eigenschaften viel, viel einfacher zu debuggen sind als private Felder. Ich möchte häufig Dinge wissen wie "Dieses Feld wird unerwartet festgelegt. Wer ist der erste Anrufer, der dieses Feld festlegt?" und es ist viel einfacher, wenn Sie einfach einen Haltepunkt auf den Setter setzen und loslegen können. Sie können sich dort anmelden. Dort können Sie Leistungsmetriken eingeben. Sie können Konsistenzprüfungen durchführen, die im Debug-Build ausgeführt werden.

Grundsätzlich kommt es darauf an: Code ist weitaus leistungsfähiger als Daten . Jede Technik, mit der ich den Code schreiben kann, den ich brauche, ist gut. In Feldern können Sie keinen Code schreiben, in den Eigenschaften jedoch.


5
Ist "Code ist weitaus leistungsfähiger als Daten" Ihr Sprichwort? Wenn Sie googeln, werden Referenzen zurückgegeben, die auf Sie verweisen. Ich möchte es nur wissen, damit ich es richtig zitieren kann, wenn ich muss.
Joan Venge

24
@ Joan: Ich weiß es nicht. Entweder habe ich es erfunden, oder ich hörte es von jemand anderem sagen und dachte: "Wow, ich sollte das total stehlen und dann alles vergessen, wem ich es gestohlen habe."
Eric Lippert

1
+ CodeLens sagt Ihnen, wo die Eigenschaft referenziert wird
UuDdLrLrSs

43

Vielleicht gibt es einen Anwendungsfall mit verschachtelten / geerbten Klassen, oder vielleicht enthält ein get / set Logik, anstatt nur den Wert der Eigenschaft zurückzugeben

Ich persönlich benutze dies auch dann, wenn ich keine Logik für den Getter oder Setter einer Eigenschaft benötige. Die Verwendung einer Eigenschaft, auch einer privaten, hilft dabei, Ihren Code zukunftssicher zu machen, sodass Sie die Logik bei Bedarf später einem Getter hinzufügen können.

Wenn ich der Meinung bin, dass eine Eigenschaft möglicherweise zusätzliche Logik erfordert, verpacke ich sie manchmal in eine private Eigenschaft, anstatt ein Feld zu verwenden, damit ich meinen Code später nicht ändern muss.


In einem halbbezogenen Fall (obwohl anders als Ihre Frage) verwende ich sehr häufig die privaten Setter auf öffentlichen Grundstücken:

public string Password 
{
    get; 
    private set;
}

Dies gibt Ihnen einen öffentlichen Getter, hält den Setter jedoch privat.


+1 macht Sinn: "Wenn ich der Meinung bin, dass eine Eigenschaft möglicherweise zusätzliche Logik erfordert, verpacke ich sie manchmal in eine private Eigenschaft, anstatt ein Feld zu verwenden, damit ich meinen Code später nicht ändern muss."
Edward Tanguay

7
Private Setter <3
Earlz

20

Eine gute Verwendung für private Get Only-Eigenschaften sind berechnete Werte. Mehrmals hatte ich Eigenschaften, die schreibgeschützt sind und nur eine Berechnung über andere Felder in meinem Typ durchführen. Es ist einer Methode nicht würdig und für andere Klassen nicht interessant, also ist es Privateigentum.


20

Eine verzögerte Initialisierung ist ein Ort, an dem sie ordentlich sein können, z

private Lazy<MyType> mytype = new Lazy<MyType>(/* expensive factory function */);

private MyType MyType { get { return this.mytype.Value; } }

// In C#6, you replace the last line with: private MyType MyType => myType.Value;

Dann können Sie schreiben: this.MyTypeüberall und nicht this.mytype.Valueund umfassen die Tatsache, dass es träge an einem einzigen Ort instanziiert wird.

Eine Schande ist, dass C # das Scoping des Hintergrundfelds für die Eigenschaft nicht unterstützt (dh es innerhalb der Eigenschaftsdefinition deklariert), um es vollständig auszublenden und sicherzustellen, dass immer nur über die Eigenschaft darauf zugegriffen werden kann.


2
Einverstanden wäre es, den Scoping-Aspekt dort zu haben.
Chris Marisic

5
Ich verwende dieselbe Technik häufig und habe mir auch gewünscht, dass ein Feld auf einen Codekörper beschränkt werden könnte. Es ist eine nette Funktion, aber mit niedriger Priorität.
Eric Lippert

5
@Eric Lippert - field-declarations Scoped in ist accessor-declarationsseit langem die Nummer 1 auf meiner C # -Wunschliste . Wenn Sie das in einer (tatsächlichen) zukünftigen Version entwerfen und implementieren könnten, dann werde ich Ihnen einen Kuchen backen.
Jeffrey L Whitledge

13

Die einzige Verwendung, an die ich denken kann

private bool IsPasswordSet 
{ 
     get
     {
       return !String.IsNullOrEmpty(_password);
     }
}

+1 für die nützliche Klasse von Eigenschaften, die aus anderen privaten Variablen berechnet werden
Daren Thomas

2
Warum nicht eine private Methode private bool IsPasswordSet() { return !String.IsNullOrEmpty(_password); }
Roman

10

Eigenschaften und Felder sind nicht eins zu eins. Bei einer Eigenschaft handelt es sich um die Schnittstelle einer Klasse (unabhängig davon, ob es sich um ihre öffentliche oder interne Schnittstelle handelt), während sich ein Feld um die Implementierung der Klasse handelt. Eigenschaften sollten nicht als eine Möglichkeit gesehen werden, nur Felder verfügbar zu machen, sondern als eine Möglichkeit, die Absicht und den Zweck der Klasse verfügbar zu machen.

So wie Sie Eigenschaften verwenden, um Ihren Verbrauchern einen Vertrag über die Zusammensetzung Ihrer Klasse vorzulegen, können Sie sich aus sehr ähnlichen Gründen auch einen Vertrag vorlegen. Also ja, ich benutze private Immobilien, wenn es Sinn macht. Manchmal kann eine private Eigenschaft Implementierungsdetails wie das verzögerte Laden, die Tatsache, dass eine Eigenschaft wirklich ein Konglomerat aus mehreren Feldern und Aspekten ist, oder dass eine Eigenschaft bei jedem Aufruf virtuell instanziiert werden muss (think DateTime.Now). Es gibt definitiv Zeiten, in denen es sinnvoll ist, dies auch für sich selbst im Backend der Klasse durchzusetzen.


+1: "Sie können sich aus sehr ähnlichen Gründen auch einen Vertrag vorlegen" macht Sinn
Edward Tanguay

8

Ich benutze sie in der Serialisierung, mit Dingen wie DataContractSerializeroder protobuf-net, die diese Verwendung unterstützen ( XmlSerializernicht). Dies ist nützlich, wenn Sie ein Objekt im Rahmen der Serialisierung vereinfachen müssen:

public SomeComplexType SomeProp { get;set;}
[DataMember(Order=1)]
private int SomePropProxy {
    get { return SomeProp.ToInt32(); }
    set { SomeProp = SomeComplexType.FromInt32(value); }
}

6

Eine Sache, die ich die ganze Zeit mache, ist das Speichern von "globalen" Variablen / Cache in HttpContext.Current

private static string SomeValue{
  get{
    if(HttpContext.Current.Items["MyClass:SomeValue"]==null){
      HttpContext.Current.Items["MyClass:SomeValue"]="";
    }
    return HttpContext.Current.Items["MyClass:SomeValue"];
  }
  set{
    HttpContext.Current.Items["MyClass:SomeValue"]=value;
  }
}

5

Ich benutze sie ab und zu. Sie können das Debuggen vereinfachen, wenn Sie einfach einen Haltepunkt in der Eigenschaft einfügen oder eine Protokollierungsanweisung usw. hinzufügen können.

Kann auch nützlich sein, wenn Sie später den Typ Ihrer Daten auf irgendeine Weise ändern müssen oder wenn Sie Reflektion verwenden müssen.


Ditto; Wenn ein get / set logisch ist, kann ich manchmal eine private oder geschützte Eigenschaft verwenden. Es hängt im Allgemeinen davon ab, wie viel Logik: einfache Logik, die ich in der Eigenschaft ausführen werde, viel Logik, die ich normalerweise mit einer Hilfsfunktion verwende. Was auch immer den Code am wartbarsten macht.
TechNeilogy

5

Ich verwende private Eigenschaften, um den Code für den Zugriff auf Unter-Eigenschaften zu reduzieren, die häufig verwendet werden.

    private double MonitorResolution
    {
        get { return this.Computer.Accesories.Monitor.Settings.Resolution; }
    }

Es ist nützlich, wenn es viele Untereigenschaften gibt.


2

Es ist üblich, Mitglieder nur mit get / set-Methoden zu ändern, auch private. Die Logik dahinter ist, dass Sie wissen, dass sich Ihr Get / Set immer auf eine bestimmte Art und Weise verhält (z. B. Ereignisse auslösen), was nicht sinnvoll erscheint, da diese nicht in das Eigenschaftsschema aufgenommen werden ... aber alte Gewohnheiten sterben schwer.


2

Es ist absolut sinnvoll, wenn mit dem Eigenschaftssatz oder -abruf eine Logik verknüpft ist (denken Sie an eine verzögerte Initialisierung) und die Eigenschaft an einigen Stellen in der Klasse verwendet wird.

Wenn es nur ein gerades Hintergrundfeld ist? Als guter Grund fällt mir nichts ein.


2

Ich weiß, dass diese Frage sehr alt ist, aber die folgenden Informationen waren in keiner der aktuellen Antworten enthalten.

Ich kann mir nicht vorstellen, wann ich jemals in der Lage sein müsste, intern zu bekommen, aber nicht zu setzen

Wenn Sie Ihre Abhängigkeiten einfügen, möchten Sie möglicherweise einen Getter für eine Eigenschaft und keinen Setter, da dies eine schreibgeschützte Eigenschaft bedeuten würde. Mit anderen Worten, die Eigenschaft kann nur im Konstruktor festgelegt und von keinem anderen Code innerhalb der Klasse geändert werden.

Außerdem gibt Visual Studio Professional Informationen zu einer Eigenschaft und nicht zu einem Feld aus, sodass Sie leichter sehen können, welches Feld verwendet wird.

PorpField


1

Nun, wie niemand erwähnt hat, können Sie damit Daten validieren oder Variablen sperren.

  • Validierung

    string _password;
    string Password
    {
        get { return _password; }
        set
        {
            // Validation logic.
            if (value.Length < 8)
            {
                throw new Exception("Password too short!");
            }
    
            _password = value;
        }
    }
  • Verriegeln

    object _lock = new object();
    object _lockedReference;
    object LockedReference
    { 
        get
        {
            lock (_lock)
            {
                return _lockedReference;
            }
        }
        set
        {
            lock (_lock)
            {
                _lockedReference = value;
            }
        }
    }

    Hinweis: Wenn Sie eine Referenz sperren, sperren Sie den Zugriff auf Mitglieder des referenzierten Objekts nicht.

Lazy Reference: Wenn Sie faul laden, müssen Sie es möglicherweise asynchron ausführen, wofür es heutzutage AsyncLazy gibt . Wenn Sie ältere Versionen als das Visual Studio SDK 2015 verwenden oder es nicht verwenden, können Sie auch AsyncEx AsyncLazy verwenden .


0

Einige exotischere Verwendungen expliziter Felder umfassen:

  • Sie müssen refoder outmit dem Wert verwenden - vielleicht weil es ein InterlockedZähler ist
  • Es soll ein grundlegendes Layout darstellen, beispielsweise in einem structexpliziten Layout (möglicherweise zur Zuordnung zu einem C ++ - Speicherauszug oder unsafeCode).
  • In der Vergangenheit wurde der Typ BinaryFormattermit automatischer Feldbehandlung verwendet (durch Ändern in Auto-Requisiten werden die Namen geändert und somit der Serializer beschädigt).
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.