Best Practices für Getter, Setter und Eigenschaften. Java vs. C #

91

Ich nehme gerade an einer C # -Klasse teil und versuche herauszufinden, wie ich am besten vorgehen kann. Ich habe einen Java-Hintergrund und bin daher nur mit den Best Practices von Java vertraut. Ich bin ein C # Neuling!

Wenn ich in Java ein Privateigentum habe, mache ich das;

private String name;

public void setName(String name) {
   this.name = name;
}

public String getName() {
   return this.name;
}

In C # sehe ich, dass es viele Möglichkeiten gibt, dies zu tun.

Ich kann es wie Java machen:

private string name;

public void setName(string name) {
   this.name = name;
}

public string getName() {
   return this.name;
}

Oder ich kann es so machen:

private string name;

public string Name {
   get { return name; }
   set { name = value; }
}

Oder:

public string Name { get; set; }

Welches sollte ich verwenden und welche Vorbehalte oder Feinheiten sind mit jedem Ansatz verbunden? Beim Erstellen von Klassen befolge ich allgemeine Best Practices, die ich aus Java kenne (insbesondere das Lesen von Effective Java). So bevorzuge ich zum Beispiel die Unveränderlichkeit (Setter nur bei Bedarf zur Verfügung stellen). Ich bin nur gespannt, wie diese Praktiken zu den verschiedenen Möglichkeiten passen, Setter und Getter in C # bereitzustellen. Wie würde ich Best Practices aus der Java-Welt in C # übersetzen?

BEARBEITEN

Ich habe dies als Kommentar zu Jon Skeets Antwort gepostet, aber dann wurde es lang:

Was ist mit einer nicht trivialen Eigenschaft (dh mit möglicherweise erheblicher Verarbeitung und Validierung)? Könnte ich es trotzdem über eine öffentliche Eigenschaft verfügbar machen, aber mit der in getund gekapselten Logik set? Warum sollte / sollte ich dies tun, wenn ich dedizierte Setter- und Getter-Methoden habe (mit zugehöriger Verarbeitungs- und Validierungslogik)?

Vivin Paliath
quelle

Antworten:

88

Pre-C # 6

Ich würde das letzte davon für eine triviale Eigenschaft verwenden. Beachten Sie, dass ich dies eine öffentliche Eigenschaft nennen würde, da sowohl die Getter als auch die Setter öffentlich sind.

Unveränderlichkeit ist bei automatisch implementierten Eigenschaften ein bisschen mühsam - Sie können keine Auto-Eigenschaft schreiben, die nur einen Getter hat. Das nächste, was Sie kommen können, ist:

public string Foo { get; private set; }

Das ist nicht wirklich unveränderlich ... nur außerhalb Ihrer Klasse unveränderlich. Daher möchten Sie möglicherweise stattdessen eine echte schreibgeschützte Eigenschaft verwenden:

private readonly string foo;
public string Foo { get { return foo; } }

Du willst definitiv nicht schreiben getName()und setName(). In einigen Fällen ist es sinnvoll, Get / Set-Methoden zu schreiben, anstatt Eigenschaften zu verwenden, insbesondere wenn diese teuer sein können und Sie dies hervorheben möchten. Sie möchten jedoch die .NET-Namenskonvention von PascalCase für Methoden befolgen und möchten nicht, dass eine solche triviale Eigenschaft mit normalen Methoden implementiert wird - eine Eigenschaft ist hier viel idiomatischer.

C # 6

Hurra, wir haben endlich die richtigen schreibgeschützten, automatisch implementierten Eigenschaften:

// This can only be assigned to within the constructor
public string Foo { get; }

Das gleiche gilt für Nur - Lese-Eigenschaften , die tun müssen einige Arbeit zu tun, können Sie Mitglied-bodied Eigenschaften verwenden:

public double Area => height * width;
Jon Skeet
quelle
5
Genauer gesagt: Jede Code-Überprüfung weist darauf hin, dass der Java-Weg ein Hack ist, der gültige Sprach- UND (!) Laufzeitkonstrukte umgeht und die Verwendung der Eigenschaft als Eigenschaft beendet (dh object.property = "value"). In einigen Teams würde dies zu einem netten Gespräch über die Einstellung führen - abhängig vom Dienstalter, kombiniert mit einem Anreiz, diese Einstellung bei einem Konkurrenten anzuwenden. Im Ernst, KÄMPFEN SIE NICHT DIE LANGAUGE. Zumal der Java "Weg" ein Hack ist, der gewählt wurde, um die Sprache für die Unterstützung von Immobilien nicht zu ändern.
TomTom
1
Ich denke, die gute Nachricht ist, dass meine Antwort nichts zu widersprechen scheint, was Sie erwähnt haben. Die schlechte Nachricht ist, dass deine Finger viel schneller sind als meine. Toller Einblick und vielen Dank für das zusätzliche Detail.
Jeremyyan
4
Um Ihre Bearbeitung zu beantworten: Sie können die get / set-Methoden mit beliebig viel Logik verwenden. Wir tun dies oft, insbesondere zur Validierung. Es wird jedoch empfohlen, nicht viel langsame Logik (z. B. Datenbankzugriff), gefährliche Logik (Auslösen von Ausnahmen) oder Mutation (Ändern vieler Zustände) in den Eigenschaften zu verwenden. Von einer Immobilie wird erwartet, dass sie sich mehr oder weniger wie ein einfacher Zustand verhält. Alles andere sollte angezeigt werden, indem stattdessen eine Funktion verwendet wird.
CodexArcanum
2
@rtindru: Ja, das ist mir bewusst. Es ist durchaus möglich, eine schreibgeschützte Eigenschaft zu schreiben. Sie können jedoch keine automatisch implementierte Eigenschaft ohne einen festgelegten Accessor deklarieren .
Jon Skeet
2
Ab C # 6 können Sie jetzt automatische Eigenschaften ohne festgelegten Accessor ( public int Y { get; } = y;) haben.
Scott Chamberlain
17

Wenn Sie lediglich eine Variable zum Speichern einiger Daten benötigen:

public string Name { get; set; }

Möchten Sie es schreibgeschützt anzeigen?

public string Name { get; private set; }

Oder noch besser ...

private readonly string _name;

...

public string Name { get { return _name; } }

Möchten Sie eine Wertprüfung durchführen, bevor Sie die Eigenschaft zuweisen?

public string Name 
{
   get { return m_name; }
   set
   {
      if (value == null)
         throw new ArgumentNullException("value");

      m_name = value;
   }
}

Im Allgemeinen werden GetXyz () und SetXyz () nur in bestimmten Fällen verwendet, und Sie müssen nur Ihren Darm verwenden, wenn es sich richtig anfühlt. Im Allgemeinen würde ich sagen, dass ich erwarte, dass die meisten get / set-Eigenschaften nicht viel Logik enthalten und nur sehr wenige, wenn überhaupt, unerwartete Nebenwirkungen haben. Wenn das Lesen eines Eigenschaftswerts das Aufrufen eines Dienstes oder das Abrufen von Eingaben von einem Benutzer erfordert, um das von mir angeforderte Objekt zu erstellen, würde ich es in eine Methode einbinden und es BuildXyz()eher so nennen als GetXyz().

Jeremyyan
quelle
2
Ich würde keine Ausnahmen in Eigenschaften auslösen, sondern lieber Methodensetzer mit Verträgen verwenden, um ein solches spezifisches Verhalten anzugeben. Wenn eine Eigenschaft vom Typ int ist, würde ich erwarten, dass sich jeder int qualifiziert. Etwas, das das Auslösen von Ausnahmen erfordert , ist meiner Meinung nach nicht einfach . Aufrufe vom Typ INotifyPropertyChanged sind meiner Meinung nach eher in dieser Zeile.
Flindeberg
3
Private Setter! = unveränderlich
Piedar
Piedar ist richtig. Ein privater Setter bedeutet nur, dass ich keine Zuweisungen vornehmen kann, aber ich kann es trotzdem verwenden myList.Add()(solange das Objekt Änderungen ausgesetzt ist, ist es veränderlich).
1
@flindeberg Entschuldigung, aber ich bin anderer Meinung ... Was ist, wenn Sie einen Fortschrittsbalken mit einem Min/ MaxWert haben ? Sie möchten das sicherstellen Max > Min? Ein SetRange(Min, Max)könnte Sinn machen, aber wie werden Sie diese Werte dann zurücklesen? Schreibgeschützte Min / Max-Eigenschaften? Das Auslösen von Ausnahmen für ungültige Eingaben scheint der sauberste Weg zu sein, um damit umzugehen.
Basic
12

Verwenden Sie Eigenschaften in C #, nicht get / set-Methoden. Sie sind für Ihre Bequemlichkeit da und es ist idiomatisch.

Was Ihre beiden C # -Beispiele betrifft, so ist eines einfach syntaktischer Zucker für das andere. Verwenden Sie die Eigenschaft auto, wenn Sie lediglich einen einfachen Wrapper um eine Instanzvariable benötigen. Verwenden Sie die Vollversion, wenn Sie dem Getter und / oder Setter Logik hinzufügen müssen.

Ed S.
quelle
5

In C # bevorzugen Sie Eigenschaften zum Offenlegen privater Felder für get und / oder set. Das von Ihnen erwähnte Formular ist eine Autoproperty, bei der das Abrufen und Setzen automatisch ein verstecktes Pivot-Backing-Feld für Sie generiert.

Ich bevorzuge Auto-Eigenschaften, wenn dies möglich ist, aber Sie sollten niemals ein Set / Get-Methodenpaar in C # ausführen.

James Michael Hare
quelle
5
public string Name { get; set; }

Dies ist einfach eine automatisch implementierte Eigenschaft und entspricht technisch einer normalen Eigenschaft. Beim Kompilieren wird ein Hintergrundfeld erstellt.

Alle Eigenschaften werden schließlich in Funktionen konvertiert, sodass die tatsächlich kompilierte Implementierung am Ende dieselbe ist, wie Sie es in Java gewohnt sind.

Verwenden Sie automatisch implementierte Eigenschaften, wenn Sie keine bestimmten Vorgänge für das Sicherungsfeld ausführen müssen. Verwenden Sie andernfalls eine gewöhnliche Eigenschaft. Verwenden Sie get- und set-Funktionen, wenn die Operation Nebenwirkungen hat oder rechenintensiv ist. Verwenden Sie ansonsten Eigenschaften.

Steven Jeuris
quelle
4

Unabhängig davon, welchen Weg Sie in C # wählen, ist das Endergebnis dasselbe. Sie erhalten eine Backinng-Variable mit separaten Getter- und Setter-Methoden. Wenn Sie Eigenschaften verwenden, befolgen Sie die Best Practices. Es kommt also darauf an, wie ausführlich Sie werden möchten.

Persönlich würde ich Auto-Eigenschaften wählen, die letzte Version : public string Name { get; set; }, da sie am wenigsten Platz beanspruchen. Und Sie können diese in Zukunft jederzeit erweitern, wenn Sie eine Validierung hinzufügen müssen.

Paul Sasik
quelle
4

Wann immer möglich, bevorzuge ich die Öffentlichkeit, string Name { get; set; }da sie knapp und leicht lesbar ist. Es kann jedoch Zeiten geben, in denen dies erforderlich ist

private string name;

public string Name {
   get { return name; }
   set { name = value; }
}
SquidScareMe
quelle
2
Können Sie erklären, wann und warum so etwas notwendig ist?
Jesper
Im Moment kann ich mir keinen Grund vorstellen, die 2. Version zu verwenden. Ich sagte nur "kann Zeiten sein, in denen dies notwendig ist". Ich hasse es, Absolutes zu verwenden, wenn ich nicht positiv bin.
SquidScareMe
3
Und warum ist das "lolz"? Sie können Ihre Unterstützung für einen Kommentar durch Upvoting zeigen.
SquidScareMe
1
Ein Grund für die Verwendung eines expliziten Hintergrundfelds ist, wenn Sie atomare / thread-sichere Operationen für den Wert ausführen möchten
Basic
4

In C # die bevorzugte Art und Weise ist durch Eigenschaften statt getX()und setX()Methoden. Beachten Sie außerdem, dass C # nicht erfordert, dass Eigenschaften sowohl ein get als auch ein set haben. Sie können nur get-Eigenschaften und nur set-Eigenschaften haben.

public boolean MyProperty
{
    get { return something; }
}

public boolean MyProperty
{
    set { this.something = value; }
}
Zach Johnson
quelle
4

Lassen Sie mich zunächst versuchen zu erklären, was Sie geschrieben haben:

// private member -- not a property
private string name;

/// public method -- not a property
public void setName(string name) {
   this.name = name;
}

/// public method -- not a property
public string getName() {
   return this.name;
}

// yes it is property structure before .Net 3.0
private string name;
public string Name {
   get { return name; }
   set { name = value; }
}

Diese Struktur wird heutzutage auch verwendet, ist jedoch am besten geeignet, wenn Sie zusätzliche Funktionen ausführen möchten. Wenn beispielsweise ein Wert festgelegt ist, können Sie ihn analysieren, um ihn zu aktivieren und als privates Element für den internen Gebrauch zu speichern.

Mit .net Framework 3.0

// this style is introduced, which is more common, and suppose to be best
public string Name { get; set; }

//You can more customize it
public string Name
{
    get;
    private set;    // means value could be set internally, and accessed through out
}

Wünsche dir mehr Glück in C #

Waqas Raja
quelle
3

Wie bereits erwähnt, führen alle diese Ansätze zum gleichen Ergebnis. Das Wichtigste ist, dass Sie eine Konvention auswählen und dabei bleiben. Ich bevorzuge die letzten beiden Eigenschaftsbeispiele.

Jeff LaFay
quelle
2

Verwenden Sie wie die meisten Antworten hier die automatischen Eigenschaften. Intuitiv, weniger Codezeilen und sauberer. Wenn Sie Ihre Klasse serialisieren sollten, markieren Sie das Attribut class [Serializable]/ with [DataConract]. Und wenn Sie verwenden, [DataContract]markieren Sie das Mitglied mit

[DataMember(Name="aMoreFriendlyName")]
public string Name { get; set; }

Privater oder öffentlicher Setter hängt von Ihrer Präferenz ab.

Beachten Sie auch, dass für automatische Eigenschaften sowohl Getter als auch Setter (öffentlich oder privat) erforderlich sind.

/*this is invalid*/
public string Name 
{ 
    get; 
   /* setter omitted to prove the point*/
}

Wenn Sie nur get / set möchten, erstellen Sie alternativ selbst ein Hintergrundfeld

RAM
quelle
0

Welches sollte ich verwenden und welche Vorbehalte oder Feinheiten sind mit jedem Ansatz verbunden?

Wenn Sie sich für Eigenschaften entscheiden, gibt es eine Einschränkung, die noch nicht erwähnt wurde: Bei Eigenschaften können Sie Ihre Getter oder Setter nicht parametrisieren.

Stellen Sie sich zum Beispiel vor, Sie möchten Listenelemente abrufen und gleichzeitig einen Filter anwenden. Mit einer get-Methode könnten Sie so etwas schreiben wie:

obj.getItems(filter);

Im Gegensatz dazu müssen Sie bei einer Eigenschaft zuerst alle Elemente zurückgeben

obj.items

Wenden Sie den Filter dann im nächsten Schritt an, oder Sie müssen dedizierte Eigenschaften hinzufügen, die nach verschiedenen Kriterien gefilterte Elemente verfügbar machen, wodurch Ihre API bald aufgebläht wird:

obj.itemsFilteredByX
obj.itemsFilteredByY

Was manchmal störend sein kann, ist, wenn Sie mit einer Eigenschaft begonnen haben, z. B. obj.itemsund später festgestellt haben, dass eine Getter- oder Setter-Parametrisierung erforderlich ist oder dem Benutzer der Klassen-API die Arbeit erleichtern würde. Sie müssten jetzt entweder Ihre API neu schreiben und alle Stellen in Ihrem Code ändern, die auf diese Eigenschaft zugreifen, oder eine alternative Lösung finden. Im Gegensatz dazu können Sie mit einer get-Methode z. B. obj.getItems()einfach die Signatur Ihrer Methode erweitern, um ein optionales "Konfigurations" -Objekt zu akzeptieren, zobj.getItems(options) ohne alle Stellen neu schreiben zu müssen, die Ihre Methode aufrufen.

Abgesehen davon sind (automatisch implementierte) Eigenschaften in C # immer noch sehr nützliche Verknüpfungen (aus den verschiedenen hier genannten Gründen), da die meiste Zeit möglicherweise keine Parametrisierung erforderlich ist - aber diese Einschränkung bleibt bestehen.

B12Toaster
quelle