Private Variable vs Eigentum?

41

Wenn Sie einen Wert für eine Variable innerhalb einer Klasse festlegen, werden meistens zwei Optionen angeboten:

private string myValue;
public string MyValue
{
   get { return myValue; }
   set { myValue = value; }
}

Gibt es eine Konvention, die festlegt, wie wir Variablen in unseren Klassen Werte zuweisen sollen? Wenn ich zum Beispiel eine Methode innerhalb derselben Klasse habe, sollte ich sie mithilfe der Eigenschaft oder der privaten Variablen zuweisen. Ich habe gesehen, dass es in beide Richtungen funktioniert, also habe ich mich gefragt, ob dies eine Wahl ist oder ob die Leistung ein Faktor ist (wahrscheinlich geringfügig).

Edward
quelle

Antworten:

23

Ich würde noch einen Schritt weiter gehen und es auf 3 Fälle bringen. Obwohl es Variationen gibt, sind dies die Regeln, die ich bei der C # -Programmierung die meiste Zeit verwende.

Gehen Sie in den Fällen 2 und 3 immer zum Property Accessor (nicht zur Feldvariablen). Und in Fall 1 müssen Sie diese Wahl nicht einmal treffen.

1.) Unveränderliches Eigentum (an den Konstrukteur übergeben oder zur Bauzeit erstellt). In diesem Fall verwende ich eine Feldvariable mit einer schreibgeschützten Eigenschaft. Ich wähle dies gegenüber einem privaten Setter, da ein privater Setter keine Unveränderlichkeit garantiert.

public class Abc
{ 
  private readonly int foo;

  public Abc(int fooToUse){
    foo = fooToUse;
  }

  public int Foo { get{ return foo; } }
}

2.) POCO-Variable. Eine einfache Variable, die in jedem öffentlichen / privaten Bereich abgerufen / festgelegt werden kann. In diesem Fall würde ich nur eine automatische Eigenschaft verwenden.

public class Abc
{ 
  public int Foo {get; set;}
}

3.) ViewModel-Bindungseigenschaften. Für Klassen, die INotifyPropertyChanged unterstützen, benötigen Sie meines Erachtens eine private Backing-Feldvariable.

public class Abc : INotifyPropertyChanged
{
  private int foo;

  public int Foo
  {
    get { return foo; }
    set { foo = value;  OnPropertyChanged("foo"); }
  }
}
Sheldon Warkentin
quelle
2
+1 für MVVM-Beispiel. Genau das hat die Frage ausgelöst.
Edward
4
+1: Mische 2/3 mit AOP und du hast eine wunderbare Möglichkeit INPC zu benutzen. [Benachrichtigt] public int Foo {get; einstellen; }
Steven Evers
1
@Job Für jede Klasse, die auf die Klasse zugreift, ist ein privater Setter für die Unveränderlichkeit ausreichend. Innerhalb der Klasse verhindert der private Setter jedoch nicht, dass der Wert nach der erstmaligen Erstellung wiederholt eingestellt wird. Ein Sprachfeature wie ein "Private Readonly Set" kann dieses Problem möglicherweise konzeptionell umgehen, ist jedoch nicht vorhanden.
Sheldon Warkentin
1
Ich bin neu in C #, so sagt mir, warum Gebrauch public int Foo {get; set;}statt public int Foo?
1
Wenn sich eine Klasse oder Struktur als POCO oder PODS verhält, welchen wirklichen Vorteil bietet es dann, Felder in Eigenschaften einzubinden? Ich verstehe voll und ganz, dass das Umbrechen von Feldern in Eigenschaften nützlich ist, wenn eine Klasse oder Struktur in Bezug auf ihren Inhalt Invarianten verwalten muss oder muss (möglicherweise, indem sichergestellt wird, dass andere Objekte entsprechend aktualisiert werden), aber wenn eine Klasse oder Struktur vorhanden ist Gibt an, dass Verbraucher alle Werte in beliebiger Reihenfolge ohne Einschränkung oder Nebenwirkungen schreiben dürfen. Welche nützlichen Verhaltensweisen können möglicherweise einem Mitglied-Accessor hinzugefügt werden?
Supercat
18

Im Allgemeinen würde ich sagen, dem Feld im Konstruktor zuweisen und die Eigenschaft überall sonst verwenden. Auf diese Weise wird die Eigenschaft nirgendwo vermisst, wenn jemand Funktionalität hinzufügt.

Es ist sicherlich kein Leistungsfaktor. Das Optimierungsprogramm erstellt ein einfaches Get oder Set für Sie und der endgültige MSIL-Code ist wahrscheinlich identisch.

pdr
quelle
Gibt es einen bestimmten Grund für die Verwendung des Felds im Konstruktor? Geringere Wahrscheinlichkeit von seltsamen Nebenwirkungen?
Anmeldung
4
@Sign: Ich gehe davon aus, dass Sie nicht das Risiko eingehen möchten, dass die Validierung während der Erstellung fehlschlägt, wenn auf dem Grundstück (jetzt oder in der Zukunft) eine Validierung durchgeführt wird. Die Validierung ist zu diesem Zeitpunkt nicht logisch, da nicht garantiert werden kann, dass das Objekt stabil ist, bis der Konstruktor fertig ist.
Steven Evers
@Sign: Sowohl was du gesagt hast als auch was Snorfus gesagt hat. Oder wenn ich Änderungen an einer Eigenschaft protokollieren möchte, möchte ich wahrscheinlich nicht die Anfangseinstellung protokollieren. Aber ich habe "allgemein" gesagt.
pdr
3
@Sign: Das Problem ist: Wenn die set-Methode der Eigenschaft in einer Unterklasse überschrieben werden kann, kann dies zu Nebeneffekten bei der Erstellung des Objekts oder eines inkonsistenten Objekts führen (dh: Die überschriebene Eigenschaft ist so programmiert, dass kein Wert für festgelegt wird dieses Feld). Die Verwendung von Eigenschaften in Konstruktoren ist nur dann sicher, wenn die set-Methode privat ist oder die Klasse versiegelt ist.
Diego
4

Hängt davon ab.

Zuerst sollten Sie automatische Eigenschaften bevorzugen, wenn dies möglich ist:

public string MyValue {get;set;}

Zweitens wäre der bessere Ansatz wahrscheinlich, die Eigenschaften zu verwenden. Wenn Sie dort eine Logik haben, sollten Sie diese wahrscheinlich selbst durchlaufen, insbesondere wenn es sich bei dieser Logik um eine Thread-Synchronisation handelt.

Sie sollten jedoch auch berücksichtigen, dass dies Ihre Leistung beeinträchtigen kann (ein wenig). Wenn Sie falsch synchronisieren, können Sie sich selbst blockieren, und manchmal ist es der richtige Weg, die Logik in der Eigenschaft zu umgehen.

AK_
quelle
3
Ich mag auch public string MyValue {get; private set;}.
Job
3

Nun, der direkte Ansatz wäre, ihn nur der Variablen selbst zuzuweisen, da Sie sich in einer Klassenmethode befinden und das Verhalten der Klasse sowieso steuern.

Der springende Punkt bei Eigenschaften ist jedoch, dass sie die Variable abstrahieren. Während eine so einfache Eigenschaft wie in Ihrem Beispiel für eine einfache öffentliche Membervariable keine Verwendung findet, tun (oder tun sollten) Eigenschaften in der Regel zusätzliche Dinge in ihren Gettern und Setters. Wenn Sie möchten, dass diese Aufgaben beim Ändern der Eigenschaft in der Klasse automatisch ausgeführt werden, ist es natürlich übersichtlicher, die Eigenschaft anstelle der Variablen zu bearbeiten, damit nicht jede Variablenzuweisung geändert werden muss, wenn sich das Verhalten der Eigenschaftseinstellungen ändert.

Man muss nur konzeptionell darüber nachdenken. Die Eigenschaft ist eigentlich ein Handle für den Zugriff auf einen internen Zustand des Objekts, der aus mehreren Mitgliedsvariablen bestehen kann. Sie müssen sich also fragen, ob Sie den zugrunde liegenden internen Zustand (oder nur einen Teil davon) oder die abstrakte Eigenschaft, die diesen Zustand insgesamt darstellt, ändern möchten. In den meisten Fällen handelt es sich tatsächlich um die letztere Eigenschaft, da Sie normalerweise möchten, dass Ihr Objekt immer diesen Zustand aufweist ein konsistenter Zustand.

Chris sagt Reinstate Monica
quelle
2

Wenn die Wahrscheinlichkeit besteht, dass sich die Implementierung dieser Eigenschaft get / set später ändert (Sie möchten beispielsweise beim Aufrufen ein Ereignis setauslösen oder Ihrer getFunktion später einen verzögerten Auswertungsmechanismus hinzufügen ), ist dies möglicherweise eine gute Idee dass Ihr Code innerhalb der Klasse die Eigenschaft in fast allen Fällen verwendet, mit Ausnahme der - höchstwahrscheinlich seltenen - Fälle, in denen Sie ausdrücklich nicht möchten, dass diese Ereignis- oder verzögerten Auswertungsmechanismen verwendet werden.

Wie auch immer, was auch immer Sie tun werden, es besteht eine gute Chance, dass Sie, wenn Sie die Eigenschaftsimplementierung später so ändern, alle Stellen in Ihrer Klasse untersuchen müssen, die auf diese Eigenschaft zugreifen, um zu überprüfen, ob wirklich auf die Eigenschaft zugegriffen werden soll oder nicht private Variable soll verwendet werden.

Doc Brown
quelle
2

Ich benutze immer das öffentliche Eigentum.

Oft wird der setMethode einer Eigenschaft eine Logik hinzugefügt, die immer ausgeführt werden soll, wenn die Eigenschaft festgelegt wird. Wenn Sie stattdessen das private Feld festlegen, umgeht der öffentliche Setter die dortige Logik.

Sie haben einen Kommentar zu MVVM, der zu dieser Frage führt, und ich bin der Meinung, dass dies umso wichtiger ist, wenn Sie mit MVVM arbeiten. Bei vielen Objekten wird eine PropertyChangeBenachrichtigung an den Setter gesendet, und andere Objekte können dieses Ereignis abonnieren, um eine Aktion auszuführen, wenn sich bestimmte Eigenschaften ändern. Wenn Sie die private Variable festlegen, werden diese Aktionen nur ausgeführt, wenn Sie das PropertyChangedEreignis auch manuell auslösen .

Rachel
quelle
+1 Ja, in den meisten Fällen (MVVM) ist das PropertyChanged-Ereignis ein Muss. Und das kann nur in einem Grundstück gefeuert werden. Gute Erklärung.
Edward
1

Im Allgemeinen liegt es an Ihnen, was Sie mit einer Eigenschaft und ihrem Hintergrundfeld beim Abrufen / Einstellen tun sollten.

In den meisten Fällen sollten Sie, um über den gesamten Code hinweg konsistent zu sein, öffentliche Zugriffsmethoden verwenden, wo immer diese verfügbar und angemessen sind. Dadurch können Sie mit minimalen Codeänderungen umgestalten. Wenn die Methode, mit der diese Einstellung vorgenommen wird, aus der Klasse entfernt und an einer anderen Stelle platziert werden muss, an der das Hintergrundfeld nicht mehr verfügbar ist (wie bei einer Basisklasse), wen interessiert das? Sie verwenden etwas, das überall dort verfügbar ist, wo die Klasse selbst die Arbeit erledigt. Das Hintergrundfeld ist in den meisten Fällen ein Implementierungsdetail. Niemand außerhalb Ihrer Klasse sollte wissen, dass es existiert.

Die Hauptsituation, an die ich denken kann, wenn Sie das Hintergrundfeld und NICHT den Eigenschafts-Accessor verwenden sollten, ist, wenn der Accessor über zusätzliche Logik verfügt (Validierung oder Aktualisierung anderer Statusinformationen in der Klasse), die Sie nicht ausführen möchten. Die anfängliche Besetzung eines Objekts ist ein Beispiel. Möglicherweise haben Sie eine Klasse, die zwei Eigenschaftswerte verwendet, um einen dritten zu berechnen, der ebenfalls in einem Hintergrundfeld gespeichert ist (aus Persistenzgründen). Beim Initialisieren einer neuen Kopie dieses Objekts mit Daten aus der Datenbank können sich die Eigenschafts-Accessoren, die jeweils den dritten Wert neu berechnen, beschweren, wenn der andere erforderliche Wert nicht festgelegt ist. Indem Sie mithilfe der Hintergrundfelder die Anfangswerte dieser zwei (oder drei) Eigenschaften festlegen, umgehen Sie die Überprüfungs- / Berechnungslogik, bis sich die Instanz in einem ausreichend konsistenten Zustand befindet, damit die Logik normal funktioniert.

KeithS
quelle
0

Verwenden Sie immer diejenige, die Sinn macht. Ja, ich weiß, das klingt ziemlich falsch, bis es eine Nichtantwort ist.

Der Punkt der Eigenschaften besteht darin, eine Schnittstelle bereitzustellen, über die Sie sicher auf ein Datenmodell zugreifen können. In den meisten Situationen möchten Sie immer sicher über diese Schnittstelle auf das Datenmodell zugreifen, z.

public Foo Bar
{
  get { return _bar; }
  set { _bar = doSomethingTo(value); }
}

In anderen Situationen können Sie jedoch einfach eine Eigenschaft als Ansicht eines Datenmodells verwenden:

public Double SomeAngleDegrees
{
  get { return SomeAngleRadians * 180 / PI; }
  set { SomeAngleRadians = value * PI / 180; }
}

Wenn es Sinn macht, die Radiant-Form von zu verwenden SomeAngle, dann verwenden Sie sie auf jeden Fall.

Am Ende solltest du deine eigene Kool-Hilfe trinken. Ihre öffentlich zugängliche API sollte stabil genug sein, um intern zu arbeiten.

zzzzBov
quelle