Soll ich Objekte mit oder ohne private Felder bevorzugen?

16

Die Codebasis, in der ich gerade arbeite, verwendet standardmäßig private Felder und öffentliche Eigenschaften. Zum Beispiel haben die meisten Klassen ihre Mitglieder wie folgt definiert:

// Fields
private double _foo;
private double _bar;
private double _baz;

// Properties
public double Foo
{
    get{ return _foo; }
    set{ _foo = value; }
}

public double Bar
{
    get{ return _bar; }
    set{ _bar = value; }
}

public double Baz
{
    get{ return _baz; }
}

Ich weiß, dass diese ohne ihre internen privaten Eigenschaften umgeschrieben werden können:

public double Foo{ get; set; }
public double Bar{ get; set; }
public double Baz{ get; private set; }

Ich hätte gerne einen Input dazu:

  • Gibt es einen guten Grund, den älteren, expliziteren Stil dem neueren, prägnanteren vorzuziehen?
  • Soll ich neue Klassen mit dem prägnanten Stil schreiben oder aus Gründen der Konsistenz versuchen, mit dem älteren Code übereinzustimmen? Ist Konsistenz in diesem Fall ausreichend, um das ältere Format zu rechtfertigen?
KChaloux
quelle
Siehe diese Frage und Antwort .
Peter K.
@PeterK. Informativ. Antwortet nicht, ob ich mich darum kümmern soll, den Stil des restlichen Programms beizubehalten, oder ob es ein Detail ist, das klein genug ist, um keine Rolle zu spielen.
KChaloux
@KChaloux: Verstanden! Deshalb ist es ein Kommentar, keine Antwort. :-)
Peter K.
@PeterK. Fair 'nuff = p
KChaloux
1
ein weiterer Grund , nicht zu ändern ist , wenn etwas über den Draht mit WCF übergeben wird - automatische Eigenschaften Vertragsfragen (aufgrund der ungültigen Zeichen in den Trägerfeldnamen von .NET erstellt) haben
Leon

Antworten:

16

Es gibt einige Fälle, in denen der sogenannte "ältere" Stil noch erforderlich ist:

A: Unveränderliche Typen unter Verwendung der von der Sprache bereitgestellten Unveränderlichkeit. Der readonlyModifikator in C # friert diesen Wert nach der Erstellung ein. Es gibt (noch) keine Möglichkeit, dies mit automatisch implementierten Eigenschaften nachzuahmen.

public sealed class FooBarBazInator
{
    private readonly double foo;

    public FooBarBazInator(double foo)
    {
        this.foo = foo;
    }

    public double Foo
    {
        get
        {
            return this.foo;
        }
    }
}

B: Ihre Getter / Setter haben irgendeine Logik. WPF- und Silverlight-Code (und ähnlicher Code), die an Ihre Klassen gebunden sind, werden folgendermaßen implementiert INotifyPropertyChanged:

public class FooBarBazInator : INotifyPropertyChanged
{
    private double foo;

    public event PropertyChangedEventHandler PropertyChanged;

    public double Foo
    {
        get
        {
            return this.foo;
        }

        set
        {
            this.foo = value;
            this.RaisePropertyChanged("Foo");
        }
    }

    private void RaisePropertyChanged(string propertyName)
    {
        var propertyChanged = this.PropertyChanged;

        if (propertyChanged != null)
        {
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Ansonsten würde ich sagen, benutze den neuen Stil. Hält Ihren Code übersichtlich und bietet weniger Fläche, auf der sich Bugs einschleichen können. Außerdem ist der Code nicht mit Signaturen kompatibel, falls er jemals geändert werden muss, um Logik einzuschließen.

Jesse C. Slicer
quelle
2
Alle scheinen sich einig zu sein, mit dem neuen Stil umzugehen. Sie erhalten eine +1 und eine Antwort, wenn Sie mir die Notwendigkeit eines Feldes mit dem readonlyModifikator mitteilen, von dem ich nichts wusste. = D
KChaloux
3
Einige (einschließlich mir) betrachten Auto-Eigenschaften mit privatem Accessor als "gut genug" für unveränderliche Eigenschaften. Sie sind zwar nicht wirklich unveränderlich, aber Sie müssen nur darauf achten, den Setter nicht außerhalb des Konstruktors zu verwenden.
Svick
2
eine andere reaon ist , wenn Sie den Wert übergeben wollen , wie refdie keine Arbeit mit Eigenschaften, so dass Sie das Feld benötigen
Stijn
1
Ein Tool wie Fody kann implementiert werden, INotifyPropertyChangedohne dass explizite Hintergrundfelder erforderlich sind. github.com/Fody/PropertyChanged
Sebastian Redl
3
Beachten Sie: C # 6 ermöglicht "Nur-Getter-Auto-Eigenschaften", die in einer einzigen Zeile angeben, was mein Beispiel "A" tut.
Jesse C. Slicer
6

Ich würde sagen, dass explizites Backing Field nur nutzloses Zeug ist: Es macht Ihren Code länger und schwieriger zu lesen. Es fügt auch Fehlerpotential hinzu:

public double Bar
{
    get { return _baz; }
    set { _bar = value; }
}

Ich denke, ein Fehler wie dieser könnte ziemlich leicht passieren und wäre ziemlich schwer zu erkennen.

Und wenn Sie Konsistenz wünschen, gehen Sie umgekehrt vor: Ändern Sie den Code, der Sicherungsfelder verwendet, in Code, der automatische Eigenschaften verwendet. Dies ist besonders einfach, wenn Sie ein Refactoring-Tool wie ReSharper verwenden (und wenn Sie so etwas nicht verwenden, sollten Sie anfangen).

Natürlich gibt es immer noch Fälle, in denen ein Hintergrundfeld erforderlich ist, aber ich denke, das ist für diese Frage nicht relevant.

svick
quelle
Ich habe das tatsächlich auf ein paar der alten Felder gemacht, als ich Code umgestaltet habe ... es ist ein Schmerz.
KChaloux
1

Obwohl die Frage ziemlich alt ist, habe ich darüber nachgedacht, einige Punkte hinzuzufügen, die ich aus einem Buch gelesen habe, das es wert ist, hier erwähnt zu werden.

  1. Die Laufzeit-Serialisierungsmodule behalten den Namen des in einem serialisierten Stream abgelegten Dokuments bei. Der Name des Sicherungsfelds für eine automatisch implementierte Eigenschaft (AIP) wird vom Compiler festgelegt. Er kann den Namen dieses Sicherungsfelds bei jeder Neukompilierung des Codes ändern, wodurch die Möglichkeit der Deserialisierung von Instanzen aller Typen negiert wird, die ein enthalten AIP. Verwenden Sie AIP daher nicht für Typen, die Sie serialisieren oder deserialisieren möchten.

  2. Beim Debuggen können Sie keinen Haltepunkt für eine AIP-Methode zum Abrufen oder Festlegen festlegen, sodass Sie nicht leicht erkennen können, wann eine Anwendung diese Eigenschaft abruft oder festlegt. Sie können Haltepunkte für manuell implementierte Haltepunkte festlegen

RAM
quelle
3
# 1 - Die meisten Serializer ignorieren standardmäßig private Eigenschaften / Felder. Ich bin nie auf die von Ihnen erwähnten Probleme gestoßen. # 2 - Dies ist in VS2015 behoben und kann in VS2013 umgangen werden. Siehe diesen Artikel des Visual Studio Magazine .
Brian
-3

Gibt es einen guten Grund, den älteren, expliziteren Stil dem neueren, prägnanteren vorzuziehen?

Nicht wirklich. Obwohl ich behaupten würde, dass Sie jedes Mal, wenn Sie ein Grundstück wie dieses durchquerten, ein öffentliches Feld hätten benutzen sollen.

Soll ich neue Klassen mit dem prägnanten Stil schreiben oder aus Gründen der Konsistenz versuchen, mit dem älteren Code übereinzustimmen? Ist Konsistenz in diesem Fall ausreichend, um das ältere Format zu rechtfertigen?

Unter der Annahme, dass Sie die obigen Hinweise ignoriert haben und Passthrough-Eigenschaften haben, würde ich nicht darauf abzielen, dem Stil zu entsprechen. Idealerweise würden Sie zurückgehen und den alten in den neuen Stil umgestalten, um konsistent zu sein, aber die Wahrscheinlichkeit, dass Leute diesen Code falsch lesen, ist gering.

Telastyn
quelle
@svick - Bah. Wenn Sie nicht über den Typ nachdenken, gibt es keinen Unterschied. Wenn Sie den Typ öffentlich als Service- oder Bibliotheksschnittstelle verfügbar machen, legen Sie eine Schnittstelle offen, für die ohnehin eine Eigenschaft erforderlich ist. Das OP tut keines dieser Dinge.
Telastyn
2
In diesen begrenzten Fällen gibt es keinen technischen Grund. Vergessen Sie nicht, dass sich Eigenschaften im Debugger, in Klassendiagrammen und in Intellisense anders verhalten, und genau dann, wenn Sie denken, dass Sie keine Reflektion durchführen, ist das .NET-Framework das Richtige. WinForms, WPF und einige andere Komponenten verwenden intern Reflection und behandeln Eigenschaften separat. Daher wird der Rat zur Verwendung öffentlicher Felder auf dieser Site von vielen Entwicklern nur unzureichend angenommen.
Kevin McCormick
1
@ KevinMcCormick und das Framework verhalten sich (soweit ich das verstehe) einwandfrei mit Feldern. Es ist nicht wirklich wichtig, da es jetzt Auto-Eigenschaften gibt, aber das größere Problem besteht darin, die Dinge überhaupt öffentlich zu machen. Viel zu viele Leute denken, dass das bloße Bilden von Eigenschaften sie irgendwie davon befreit, 'keine öffentlichen Felder zu machen'.
Telastyn
2
Das Framework behandelt sie unterschiedlich: Versuchen Sie, ein öffentliches Feld in EF POCO zu verwenden, es an eine DataGridView zu binden, ein PropertyGrid zu verwenden usw. Ich habe gerade in meinem Dekompiler nach Type.GetProperties / GetProperty gesucht und das Framework ruft diese Methode auf Hunderte Male, aber nicht zu GetField. Unterm Strich sind sie unterschiedlich, und die Empfehlung, Eigenschaften immer für öffentliche Daten zu verwenden, basiert teilweise auf dieser Tatsache. Dies schließt die Verwendung von Feldern nicht aus, muss jedoch begründet werden. Ihr anderer Punkt ist jedoch auf jeden Fall richtig. Eine unachtsame Offenlegung öffentlicher Daten ist immer eine schlechte Idee.
Kevin McCormick