Automatisch implementierte Getter und Setter im Vergleich zu öffentlichen Feldern

75

Ich sehe viele Beispielcodes für C # -Klassen, die dies tun:

public class Point {
    public int x { get; set; }
    public int y { get; set; }
}

Oder in älterem Code dasselbe mit einem expliziten privaten Sicherungswert und ohne die neuen automatisch implementierten Eigenschaften:

public class Point {
    private int _x;
    private int _y;

    public int x {
        get { return _x; }
        set { _x = value; }
    }

    public int y {
        get { return _y; }
        set { _y = value; }
    }
}

Meine Frage ist warum. Gibt es einen funktionalen Unterschied zwischen dem oben genannten und dem öffentlichen Erstellen dieser Mitgliederfelder wie unten?

public class Point {
    public int x;
    public int y;
}

Um es klar auszudrücken, ich verstehe den Wert von Gettern und Setzern, wenn Sie eine Übersetzung der zugrunde liegenden Daten vornehmen müssen. Aber in Fällen, in denen Sie nur die Werte weitergeben, scheint es unnötig ausführlich zu sein.

tclem
quelle

Antworten:

49

Ich stimme eher zu (dass es unnötig ausführlich erscheint), obwohl dies ein Problem war, das unser Team noch nicht gelöst hat, und daher bestehen unsere Codierungsstandards immer noch auf ausführlichen Eigenschaften für alle Klassen.

Jeff Atwood hat sich vor einigen Jahren damit befasst. Der wichtigste Punkt, den er nachträglich bemerkte, ist, dass der Wechsel von einem Feld zu einer Eigenschaft eine bahnbrechende Änderung in Ihrem Code darstellt. Alles, was es verbraucht, muss neu kompiliert werden, um mit der neuen Klassenschnittstelle zu arbeiten. Wenn also etwas außerhalb Ihrer Kontrolle Ihre Klasse verbraucht, können Probleme auftreten.

Dexter
quelle
Vielen Dank für diesen Link und überzeugen Sie mich, dass bei der Umstellung von Feld und Eigentum tatsächlich Probleme auftreten werden. Es ist seltsam für mich, da die Syntax trotzdem gleich ist.
tclem
Sie würden denken, es würde sich auf dasselbe beschränken, aber offensichtlich sind die internen Mechaniken etwas anders.
Dexter
9
In C # wird eine Eigenschaft mit get und set mit zwei Methoden wie get_PropertyName () und set_PropertyName (Typwert) kompiliert. Die Eigenschaftssyntax in C # ist nur syntaktischer Zucker. Eigenschaften können Code ausführen, daher wäre es für sie nicht sinnvoll, auf denselben Code wie ein Feld zu kompilieren.
Jason Jackson
9
Die JIT-Compiler optimieren eine Eigenschaft ohne Code so, dass sie einem Feld entspricht. Daher gibt es keinen Grund, Eigenschaften aufgrund der Kurzschrift-Syntax in C # 3.0+
Erv Walter,
31

Es ist auch viel einfacher, es später zu ändern:

public int x { get; private set; }
Brad Wilson
quelle
11
Ich ... wusste nicht einmal, dass du das kannst.
Merus
Richtig, das ist sehr hilfreich, leider können Sie das nicht in einer Schnittstelle tun
Georges
9

Es kapselt das Einstellen und Zugreifen dieser Mitglieder. Wenn ein Entwickler für den Code in einiger Zeit die Logik ändern muss, wenn auf ein Mitglied zugegriffen oder festgelegt wird, kann dies ohne Änderung des Vertrags der Klasse erfolgen.

Joseph Daigle
quelle
1
Aber wie ändert das den Vertrag der Klasse? So oder so point.x = 1;und gut point.xfunktionieren.
Ajedi32
2
@ Ajedi32 technisch sind sie nicht der gleiche Vertrag. Wenn Sie es von einem Feld in eine Eigenschaft (oder von einer Eigenschaft in ein Feld) ändern, müssen Sie alles neu kompilieren, was auf dieses Mitglied zugreift. Weil sich die kompilierte IL beim Zugriff auf eine Eigenschaft von einem Feld unterscheidet.
Joseph Daigle
8

Die Idee ist, dass selbst wenn sich die zugrunde liegende Datenstruktur ändern muss, die öffentliche Schnittstelle zur Klasse nicht geändert werden muss.

C # kann Eigenschaften und Variablen manchmal unterschiedlich behandeln. Beispielsweise können Sie Eigenschaften nicht als ref- oder out-Parameter übergeben . Wenn Sie also aus irgendeinem Grund die Datenstruktur ändern müssen und öffentliche Variablen verwendet haben und jetzt Eigenschaften verwenden müssen, muss sich Ihre Schnittstelle ändern, und jetzt wird der Code, der auf die Eigenschaft x zugreift, möglicherweise nicht mehr so ​​kompiliert, wie er war, als er variabel war x:

Point pt = new Point();
if(Int32.TryParse(userInput, out pt.x))
{
     Console.WriteLine("x = {0}", pt.x);
     Console.WriteLine("x must be a public variable! Otherwise, this won't compile.");
}

Durch die Verwendung von Eigenschaften von Anfang an wird dies vermieden, und Sie können die zugrunde liegende Implementierung beliebig optimieren, ohne den Clientcode zu beschädigen.

Rob Pilkington
quelle
3

Mit Setter and Getter können Sie eine zusätzliche Abstraktionsschicht hinzufügen. In reinem OOP sollten Sie immer über die Schnittstelle, die sie der Außenwelt zur Verfügung stellen, auf die Objekte zugreifen ...

Betrachten Sie diesen Code, der Sie in asp.net speichert und der ohne die von den Setzern und Gettern bereitgestellte Abstraktionsebene nicht möglich wäre:

class SomeControl
{

private string _SomeProperty  ;
public string SomeProperty 
{
  if ( _SomeProperty == null ) 
   return (string)Session [ "SomeProperty" ] ;
 else 
   return _SomeProperty ; 
}
}
Yordan Georgiev
quelle
3

Da automatisch implementierte Getter denselben Namen für die Eigenschaft und die tatsächlichen privaten Speichervariablen haben. Wie können Sie es in Zukunft ändern? Ich denke, der Punkt, der gesagt wird, ist, dass Sie das automatisch implementierte anstelle des Feldes verwenden, damit Sie es in Zukunft ändern können, falls Sie Logik zu Getter und Setter hinzufügen müssen.

Zum Beispiel:

public string x { get; set; }

und zum Beispiel verwenden Sie das xa bereits oft und möchten Ihren Code nicht brechen.

Wie ändere ich den Auto Getter Setter? Zum Beispiel erlaubst du für Setter nur das Einstellen eines gültigen Telefonnummernformats ... Wie änderst du den Code so, dass nur die Klasse geändert werden soll?

Meine Idee ist, eine neue private Variable hinzuzufügen und den gleichen x Getter und Setter hinzuzufügen.

private string _x;

public string x { 
    get {return _x}; 
    set {
        if (Datetime.TryParse(value)) {
            _x = value;
        }
    }; 
}

Ist es das, was Sie damit meinen, es flexibel zu machen?

Nickerchen
quelle
2

Zu berücksichtigen ist auch die Auswirkung des Wechsels zu öffentlichen Mitgliedern in Bezug auf Bindung und Serialisierung. Beide basieren häufig auf öffentlichen Eigenschaften, um Werte abzurufen und festzulegen.

TheZenker
quelle
2

Sie können auch Haltepunkte für Getter und Setter setzen, jedoch nicht für Felder.

NotDan
quelle
1

AFAIK die generierte CIL-Schnittstelle ist anders. Wenn Sie ein öffentliches Mitglied in eine Eigenschaft ändern, ändern Sie die öffentliche Schnittstelle und müssen jede Datei neu erstellen, die diese Klasse verwendet. Dies ist nicht erforderlich, wenn Sie nur die Implementierung der Getter und Setter ändern.


quelle
1

Wenn Sie nur Felder öffentlich machen, können Sie möglicherweise zu einem anämischeren Domänenmodell gelangen .

Mit freundlichen Grüßen

marcospereira
quelle
1

Es ist auch erwähnenswert, dass Sie Auto Properties nicht schreibgeschützt machen und sie nicht inline initialisieren können. Beides möchte ich in einer zukünftigen Version von .NET sehen, aber ich glaube, Sie können beides in .NET 4.0 nicht tun.

Ich verwende heutzutage nur dann ein Hintergrundfeld mit Eigenschaften, wenn meine Klasse INotifyPropertyChanged implementiert und ich das Ereignis OnPropertyChanged auslösen muss, wenn eine Eigenschaft geändert wird.

Auch in diesen Situationen setze ich die Hintergrundfelder direkt, wenn Werte von einem Konstruktor übergeben werden (es ist nicht erforderlich, das OnPropertyChangedEvent (das zu diesem Zeitpunkt ohnehin NULL wäre) auszulösen, wo immer ich die Eigenschaft selbst verwende.

MrLane
quelle
2
Das ist nicht ganz richtig. Sie können automatisch implementierte Eigenschaften schreibgeschützt (oder schreibgeschützt) haben, wenn Sie SomePropName {get; private set;}, und setzen Sie dann die Eigenschaft mit den Objektinitialisierern ..
jhexp
0

Sie wissen nie, ob Sie später möglicherweise keine Übersetzung der Daten benötigen. Sie sind darauf vorbereitet, wenn Sie Ihre Mitglieder verstecken. Benutzer Ihrer Klasse werden es nicht bemerken, wenn Sie die Übersetzung hinzufügen, da die Benutzeroberfläche dieselbe bleibt.

EricSchaefer
quelle
0

Der größte Unterschied besteht darin, dass Sie bei jeder Änderung Ihrer internen Struktur die Getter und Setter unverändert beibehalten und ihre interne Logik ändern können, ohne die Benutzer Ihrer API zu verletzen.

Gizmo
quelle
0

Wenn Sie in diesem Fall ändern müssen, wie Sie x und y erhalten, können Sie die Eigenschaften später hinzufügen. Das finde ich am verwirrendsten. Wenn Sie öffentliche Mitgliedsvariablen verwenden, können Sie diese später problemlos in eine Eigenschaft ändern und private Variablen mit den Namen _x und _y verwenden, wenn Sie den Wert intern speichern müssen.

Kibbee
quelle
0

Warum verwenden wir nicht nur öffentliche Felder anstelle von Eigenschaften und rufen dann Accessoren (get, set) auf, wenn wir keine Validierungen vornehmen müssen?

  1. Eine Eigenschaft ist ein Mitglied, das einen flexiblen Mechanismus zum Lesen oder Schreiben bietet
  2. Eigenschaften können überschrieben werden, Felder jedoch nicht.
jihed
quelle
0

Durch Hinzufügen von Getter und Setter wird die Variable zu einer Eigenschaft wie beim Arbeiten in Wpf / C #.

Wenn es sich nur um eine öffentliche Mitgliedsvariable handelt, kann von XAML nicht darauf zugegriffen werden, da es sich nicht um eine Eigenschaft handelt (obwohl es sich um eine öffentliche Mitgliedsvariable handelt).

Wenn es Setter und Getter hat, ist es über XAML zugänglich, da es jetzt eine Eigenschaft ist.

zar
quelle
-1

Setter und Getter sind im Prinzip schlecht (sie sind ein schlechter OO-Geruch - ich werde aufhören zu sagen, dass sie ein Anti-Muster sind, weil sie manchmal wirklich notwendig sind).

Nein, es gibt technisch keinen Unterschied, und wenn ich heutzutage wirklich den Zugriff auf ein Objekt teilen möchte, mache ich es gelegentlich öffentlich endgültig, anstatt einen Getter hinzuzufügen.

Die Art und Weise, wie Setter und Getter "verkauft" wurden, ist, dass Sie möglicherweise wissen müssen, dass jemand einen Wert erhält oder einen ändert - was nur bei Primitiven Sinn macht.

Eigenschaftenbeutelobjekte wie DAOs, DTOs und Anzeigeobjekte sind von dieser Regel ausgeschlossen, da dies keine Objekte in einer echten "OO Design" -Bedeutung des Wortes "Objekt" sind. (Sie denken nicht daran, "Nachrichten an ein DAO weiterzuleiten", es ist einfach ein Stapel von Attribut / Wert-Paaren).

Bill K.
quelle