Sollten Sie private Immobilien machen?


19
private string mWhatever;

private string Whatever
{
    get
    {
        return this.mWhatever;
    }
    set
    {
        this.mWhatever = value;
    }
}

Ich habe einige Leute gesehen, die Immobilien für jedes einzelne Mitglied machen, privat oder nicht ... macht das irgendeinen Sinn? Ich konnte es in 1% der Fälle als sinnvoll erachten, wenn Sie den Zugriff auf das Mitglied in der Klasse, in der es enthalten ist, steuern möchten, da es zu Inkonsistenzen führen und prüfen würde, ob das Element verwendet wird Mitglied hat einen Zugriff oder nicht (da Sie im Bereich der Klasse Zugriff auf beide haben).

Antworten:


17

Kurze Antwort: Ja , wenn Bedarf besteht. Verwenden Sie andernfalls einen Getter und Setter wie Auto-Implemented Propertyprivate string Whatever { get; set;}

  • Dies ist sehr praktisch, wenn Sie einen engen Domänenansatz verwenden
  • Es ist auch praktisch, wenn beim Einstellen des Werts eine bestimmte Logik überprüft werden soll

Hier finden Sie eine vollständige Beschreibung, wann Sie private Setter verwenden würden: Verwendung von C # -Eigenschaften .


Es ist auch sehr einfach, die Immobilie bei Bedarf später zu veröffentlichen.
Tom Pickles

4
Was ist ein enger Domain Ansatz ?
Trisped

1
Ich bezog mich auf einen privaten Setter in einer Eigenschaft der Klasse. Sie sollte den direkten Zugriff auf den Einstellwert des Objekts verhindern und eine logische Prüfung vor dem Einstellen des Werts erleichtern.
EL Yusubov

Private Variablen mit öffentlichen Setzern / Gettern (auch wenn sie nur naiv sind) haben außerdem den Vorteil, dass die Signatur beim Kompilieren Ihres Codes in eine Bibliothek unverändert bleibt, wenn Sie Ihre naiven Setzer / Gettern durch solche mit Side-Gettern ersetzen. Effekt usw., aber wenn Sie stattdessen eine einfache öffentliche Variable verwenden, ändert sich die Bibliothekssignatur, wenn Sie sie in einen privaten, nicht naiven Setter / Getter ändern. .. was bedeuten würde, dass, wenn sich Ihre Bibliothek auf diese Weise ändern würde, die Konsumenten Ihrer Bibliothek neu kompilieren müssten.
Orion Elenzil

12

Einige gute Antworten hier bereits, aber ich denke, die meisten von ihnen vermissen einen Punkt Ihrer Frage:

Ich habe einige Leute gesehen, die Immobilien für jedes einzelne Mitglied machen, privat oder nicht ... macht das irgendeinen Sinn?

Ich denke, dass dies im Vorfeld selten für jedes einzelne Mitglied notwendig ist . Beginnen mit

 private string Whatever;

Wenn Sie später zu einem Punkt kommen, an dem Sie eine Kapselung oder einen bedingten Haltepunkt für dieses bestimmte Element benötigen , können Sie es dennoch durch eine Eigenschaft mit demselben Namen ersetzen - in den meisten Fällen, ohne den verwendeten Code zu ändern Whatever. Aber Vorsicht, es gibt subtile Unterschiede, siehe die Antwort von Brian Rasmussen in diesem SO-Beitrag (Link auch von Emmad Kareem, +1).


+1, auch Sie sind richtig, die meisten Antworten verfehlt die ursprünglichen
Fragenpunkte

Die nachträgliche Umgestaltung eines Feldes zu einem Grundstück ist ein Albtraum. Sie müssen alle Consumer neu kompilieren. Das Ändern einer automatischen Eigenschaft in eine benutzerdefinierte Logik ist ein Kinderspiel. Wenn der Refactor des Codes nur eine geringe Chance hat, wird es viel weniger Arbeit bedeuten, wenn Sie nur Eigenschaften von Anfang an verwenden.
Dan

@Dan: in den meisten projekten habe ich gesehen, dass der aufwand ziemlich gleich ist, du musst in beiden fällen neu kompilieren . Dennoch glaube ich, dass Sie Recht haben, dass die Verwendung von Eigenschaften, insbesondere von Auto-Eigenschaften, dazu beitragen kann, einige subtile Probleme bei der Reflektion oder der Verwendung von Elementvariablen in "out" -Parametern zu vermeiden.
Doc Brown

@DocBrown Auf jeden Fall ist es keine allumfassende Aussage. Es gibt viele Gründe, es einfach zu halten und ein Feld zu verwenden. Wenn Sie jedoch einen Refaktor vorhersagen können, sind auch die subtilen Probleme eine Überlegung wert.
Dan

7

Einer der größten Vorteile dieses Ansatzes besteht darin, dass Sie die Kontrolle darüber haben, wann sich Ihre Variablen ändern .

Folgendes berücksichtigen.
Sie debuggen ein großes Projekt. Eine bestimmte Methode löst eine Ausnahme aus. Sie legen einen Haltepunkt fest und stellen fest, dass eine bestimmte Elementvariable einen falschen Wert hat. Angenommen, Ihr Code stützt sich sehr stark auf diese Variable, und Sie finden Hunderte von Schreibverwendungen ( Spaghetti-Szenario ). Sie können also nicht herausfinden, welche dieser Verwendungen einen schlechten Wert haben.
Wenn der Code mehrere Threads enthält, kann das Debuggen zu einem echten Albtraum werden.

Mit property können Sie einen Haltepunkt in einem Setter setzen . In Kombination mit einer Bedingung kann es in einem Durchgang festgenagelt werden.

VC ++ verfügt über Datenhaltepunkte , ist jedoch nur für nicht verwalteten Code verfügbar.


1

Wenn Sie keine Eigenschaft verwenden, können Sie auch ein Feld zum Speichern variabler Daten verwenden. Eigenschaften und Felder weisen erhebliche Unterschiede auf. Die meisten dieser Unterschiede sind Felder im Vergleich zu Eigenschaften . Ich schlage vor, Sie untersuchen die Unterschiede und treffen dann Ihre Wahl entsprechend Ihren Anwendungsanforderungen. Bedenken Sie jedoch, dass die Verwendung von Feldern anstelle von Eigenschaften aufgrund ihrer Art und ihres Mangels nicht die übliche OO-Praxis ist.


1

Private Eigenschaften können sehr nützlich sein, um das Verhalten von Dingen zu kapseln, die innerhalb Ihrer Klasse liegen. Nur weil sie privat sind, heißt das nicht, dass Sie den syntaktischen Zucker, den Ihnen die Eigenschaften verleihen, nicht ausnutzen sollten.


Das angegebene Beispiel ist jedoch schlecht. Boilerplate Property Getter und Setter wie dieses sind fast immer eine schlechte Idee. Wenn Sie C # 3.0 oder höher verwenden, sind automatische Eigenschaften eine viel bessere Idee:

private string Whatever { get; set; };

Dies ist kürzer, sauberer und viel besser lesbar. Tatsächlich ist es kaum länger, als nur die Hintergrundvariable zu deklarieren.

Am wichtigsten ist jedoch, dass automatische Eigenschaften jederzeit in vollständige Eigenschaften konvertiert werden können, ohne die Semantik des restlichen Programms zu ändern! Wenn Sie also eine Validierung oder Fehlerbehandlung hinzufügen müssen, können Sie dies auf einfache Weise tun.


So wie es ist, dachte ich immer, dass es eine Schande ist, dass Sie keine schreibgeschützte Eigenschaft haben können, um zu erzwingen, dass nur in den Wert im Konstruktor geschrieben werden kann (ich bevorzuge unveränderliche Typen ), aber dies wurde in C # 6.0 als hinzugefügt "Auto-Property Initializers", mit denen Sie Folgendes tun können:

private string Whatever { get; } = ...;

oder

private string Whatever { get; };

zusammen mit

    Whatever = ...;

im Konstruktor.


0

Dies ist für private Eigenschaften nicht unbedingt erforderlich, ermöglicht jedoch das Ändern des Abrufs / Festlegens des zugrunde liegenden Werts für die Eigenschaft, ohne den Zugriff darauf zu ändern.

Ändern Sie beispielsweise, wie der zugrunde liegende Wert festgelegt wird:

private string Whatever
{
    get
    {
        return this.mWhatever;
    }
    set
    {
        this.mWhatever = string.IsNullOrEmpty(value) ? string.Empty : value;
    }
}

0

Ja

Ich persönlich verwende dies als Caching-Mechanismus, und wir könnten es als Caching auf Eigenschaftsebene bezeichnen .

private List<User> users;
private List<User> Users
{
    get
    {
        if(users == null) users = new UserManager().GetUsers();
        return users;
    }
}

Jetzt verwende ich an anderen Stellen in meiner Klasse UsersEigenschaft anstelle von usersFeld.

Eine andere mögliche Situation könnte sein, wenn Sie eine Logik in einem Feld implementieren möchten, jedoch zentral in einer Klasse. Auf diese Weise können Sie sowohl eine GetFoo()Methode als auch eine FooEigenschaft für ein fooFeld erstellen .

private string foo;
private string Foo
{
    get
    {
        return "Mr. " + foo;
    }
}

0

Noch etwas zu beachten:

Tatsächliche Eigenschaften sind ein Implementierungsdetail. Eines der Ziele von OOP ist es, die Offenlegung von Implementierungsdetails so gering wie möglich zu halten.

Der Grund dafür ist, dass Sie weitaus mehr Kontrolle haben, wenn Sie die Eigenschaften ausblenden und sie nur durch Getter und Setter verfügbar machen. Beispielsweise kann Ihr Getter seine Eingabe validieren und verhindern, dass die Eigenschaft in einen ungültigen Zustand versetzt wird. Wenn die Möglichkeit, den Wert zu ändern, zeitkritisch ist, kann der Einsteller auch davor schützen. Der Getter kann, sollte es aus irgendeinem Grund teuer sein, den tatsächlichen Wert zu ermitteln, eine verzögerte Initialisierung und / oder Zwischenspeicherung durchführen.

Wenn Getter und Setter aufgedeckt und die Eigenschaften ausgeblendet werden, brauchen Sie manchmal überhaupt keine Eigenschaft. Ihr Getter kann den Wert beim Aufruf berechnen oder die Aufgabe an eine andere Stelle delegieren oder alles, was dazu beiträgt, dass sie der Spezifikation entspricht. An Ihrer Klasse ist es wichtig, auf welche Informationen Sie zugreifen können, und nicht, wie diese Informationen intern dargestellt werden.

Wenn nichts außerhalb der Klasse negative Konsequenzen hat, wenn es direkt auf eine Eigenschaft zugreifen kann, sollten Sie sie einfach öffentlich machen. Nach meiner Erfahrung sind solche Fälle jedoch relativ selten.


0

Ich weiß, dass dies eine alte Frage ist und dass alles , was @ Yusubov sagte, richtig ist, aber in Bezug auf seinen zweiten Aufzählungspunkt über bestimmte Logik im Setter, und da ich niemanden sah, der dies ausdrücklich erwähnte, wäre dies ein perfektes Beispiel für Ihre Klasse implementiert die INotifyPropertyChangedSchnittstelle.

Dies wird in WCF und Silverlight mit einer Tonne verwendet , um zu ermitteln , wann sich eine bestimmte Eigenschaft über die Logik in ihrem Setter geändert hat, wie in der folgenden Klasse:

public class Settings : INotifyPropertyChanged
{
    public Settings() 
    { 
        this.CountryField = String.Empty; 
    }

    private string CountryField;
    public string Country
    {
        get { return this.CountryField; }
        set
        {
            if (Object.ReferenceEquals(this.CountryField, value)) { return; }

            this.CountryField = value;
            this.RaisePropertyChanged("Country");
        } 
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler propertyChanged = this.PropertyChanged;

        if (propertyChanged != null)
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}
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.