private Setter verstehen


91

Ich verstehe nicht die Notwendigkeit privater Setter, die mit C # 2 begonnen haben.

Wenn ich eine Setter-Methode für mich habe, kann der Benutzer einige Variablen in dieser Klasse festlegen. Dabei werden die Variablen nicht direkt den Benutzern zugänglich gemacht. Stattdessen lassen wir sie dies durch diese Public-Setter-Methode tun.

Dies verwendet für mich "Kapselung". Es gibt einige Argumente, die behaupten, dass Sie mit privaten Setzern die Kapselung anwenden können.

Benutze ich die Kapselung nicht mit öffentlichen Setter-Methoden? Warum brauchen wir private Setter?

Was ist der Unterschied zwischen einer unveränderlichen Klasse und einer Klasse mit privaten Setzern?


1
Ich habe die privaten Setter sehr geliebt - hat mir geholfen, hässliche Klassen neu zu faktorisieren. Sie machen es auch unmöglich, eine nicht konstante Instanzvariable auf einmal so zu deklarieren und zu setzen: private File settingsFile = null;und dann in einem der Konstruktoren : if (settingsFile == null) { settingsFile = GetSettingsFile() };. Solche Refactoring-Codes haben mich manchmal zum Weinen gebracht :). Nur weil Sie ein Mitglied vor dem Konstruktor festlegen können, bedeutet dies nicht, dass Sie dies tun sollten, da es bei mehreren Konstruktoren SCHWER ist, der Logik zu folgen. Private Setter zwingen Sie, Werte innerhalb des Konstruktors oder höher festzulegen.
Hamish Grubijan

Antworten:


260

Logisch.

Das Vorhandensein eines privaten Setzers ist darauf zurückzuführen, dass Sie die Eigenschaft auto verwenden können:

public int MyProperty { get; set; }

Was würden Sie tun, wenn Sie es schreibgeschützt machen möchten?

public int MyProperty { get; }

Oh Mist!! Ich kann von meiner eigenen Klasse aus nicht darauf zugreifen. Ich sollte es wie eine normale Eigenschaft erstellen:

private int myProperty;
public int MyProperty { get { return myProperty; } }

Hmm ... aber ich habe die Funktion "Auto Property" verloren ...

public int MyProperty { get; private set; }

AHHH .. das ist besser !!


Vielen Dank. Das macht wieder Sinn
Dene

3
@ktutnik Vielen Dank, dass Sie dies so angelegt haben, wie Sie es getan haben. Das macht jetzt auch für mich Sinn!
Vivek M. Chawla

3
Hervorragend illustrierte Antwort.
Imnk

3
Oh crap!! I can't access it from my own classAb C # 6.0 gilt dies nur außerhalb der Initialisierungsphase. Siehe meine Antwort stackoverflow.com/a/34223746/198797
tsemer

1
Das Hinzufügen zu # tsemers Antwort mit c # 6 {get; }ist NICHT gleichbedeutend mit { get; private set; }. Für den ersten Weg property.GetSetMethod(true)kehrt nullder letztere zurück true. Das hat mich überrascht.
Emragins

37

Ein privater Setter ist nützlich, wenn Sie eine schreibgeschützte Eigenschaft haben und die Hintergrundvariable nicht explizit deklarieren möchten.

So:

public int MyProperty
{
    get; private set;
}

ist das gleiche wie:

private int myProperty;
public int MyProperty
{
    get { return myProperty; }
}

Bei nicht automatisch implementierten Eigenschaften können Sie die Eigenschaft innerhalb Ihrer Klasse konsistent festlegen, sodass Sie sie nur an einer Stelle haben, wenn Sie eine Validierung usw. benötigen.

Um Ihre letzte Frage zu beantworten, hat der MSDN Folgendes zu privaten Setzern zu sagen:

Für kleine Klassen oder Strukturen, die nur eine Reihe von Werten (Daten) kapseln und wenig oder gar kein Verhalten aufweisen, wird empfohlen, die Objekte unveränderlich zu machen, indem der eingestellte Accessor als privat deklariert wird.

Auf der MSDN-Seite unter Automatisch implementierte Eigenschaften


1
Es tut mir leid, dass ich den Mehrwert immer noch nicht sehe, weil ich private Setter habe. Wenn wir den Setter nicht exponieren wollen, haben wir nur Getter. Wenn wir einen Validator hinzufügen möchten, können wir einen öffentlichen Setter haben und ihm eine Validierung hinzufügen. Warum brauchen wir einen Setter, der nicht zugänglich ist? So wie ich es verstehe, ist es wie "Nimm dieses Auto, aber du kannst es nicht fahren". Warum willst du mir das Auto geben, wenn ich es sowieso nicht fahren werde
Dene

@Dene - Das kannst du sicher, da es nicht falsch ist. Automatisch implementierte Eigenschaften sind nicht obligatorisch.
ChrisF

Ich weiß, dass es nichts Falsches ist, so zu tun, wie ich es ausgedrückt habe. Es ist nur für mich, die Verbesserung von C # 2 zu schätzen. Es scheint viel Hype zu geben, aber ich kann es einfach nicht fühlen oder den Wert sehen.
Dene

@Dene, ich habe den ganzen Hype darüber verpasst. Aber als ich endlich sah, dass dies möglich war, war ich glücklich, weil ich wusste, wie man einige lange Klassen aus der .Net 1.1-Ära aufräumt. Während Sie wahrscheinlich den Wert der Eigenschaft verwenden können, die noch nicht festgelegt wurde, ist dies weniger natürlich als die Verwendung des Werts einer Instanzmitgliedsvariablen, die auf null festgelegt wurde.
Hamish Grubijan

Es vereinfacht den Code. Genau wie Auto-Eigenschaften. Bei vielen der nachfolgenden Aktualisierungen von C # geht es darum, den Code knapper und daher lesbarer zu machen. Sie können die Dinge aber auch anders machen, wenn Sie möchten - Abwärtskompatibilität scheint ebenfalls ein großes Ziel zu sein. Ich denke, es ist Geschmackssache.
Niico

18

Es ist ziemlich einfach. Mit privaten Setzern können Sie schreibgeschützte öffentliche oder geschützte Eigenschaften erstellen.

Das ist es. Das ist der einzige Grund.

Ja, Sie können eine schreibgeschützte Eigenschaft erstellen, indem Sie nur den Getter angeben. Bei automatisch implementierten Eigenschaften müssen Sie jedoch sowohl get als auch set angeben. Wenn Sie also möchten, dass eine automatisch implementierte Eigenschaft schreibgeschützt ist, müssen Sie sie verwenden private Setter. Es gibt keinen anderen Weg, dies zu tun.

Private Setter wurden zwar nicht speziell für automatisch implementierte schreibgeschützte Eigenschaften erstellt, ihre Verwendung ist jedoch aus anderen Gründen etwas esoterischer, da sie sich hauptsächlich auf schreibgeschützte Eigenschaften und die Verwendung von Reflexion und Serialisierung konzentrieren.


2
Vielen Dank "Wenn eine automatisch implementierte Eigenschaft schreibgeschützt sein soll, müssen Sie private Setter verwenden". Das macht Sinn für mich
Dene

17

Mit der Einführung von C # 6.0 und der Syntax für Auto-Property Initializers werden private Setter nicht mehr für Eigenschaften benötigt, die nur bei der Initialisierung entweder inline oder innerhalb des Konstruktors festgelegt werden.

Diese neuen Syntaxen werden jetzt kompiliert:

Inline initialisierte Eigenschaft

public class MyClass1 {
  public string MyProperty { get; } = "Aloha!"
}

Vom Konstruktor initialisierte Eigenschaft

public class MyClass2 {
  public string MyProperty { get; }

  public MyClass2(string myProperty) {
    MyProperty = myProperty;
  }
}

3
@Ziggler, in der Tat nicht. OP fragt es auch nicht. Er versteht einfach nicht, wie wichtig es ist, sie zu haben. Dies antwortet: "Sie müssen sie in diesem Szenario nicht mehr haben".
Tsemer

6

Ich verstehe nicht die Notwendigkeit privater Setter, die mit C # 2 begonnen haben.

Mit der Rechnungsklasse kann der Benutzer beispielsweise Elemente zur Items-Eigenschaft hinzufügen oder daraus entfernen, der Benutzer kann jedoch die Items-Referenz nicht ändern (dh der Benutzer kann die Items-Eigenschaft nicht einer anderen Item-List-Objektinstanz zuweisen).


public class Item
{
  public string item_code;
  public int qty;

  public Item(string i, int q)
  {
    this.item_code = i;
    this.qty = q;
  }
}

public class Invoice
{
  public List Items { get; private set; }

  public Invoice()
  {
    this.Items = new List();
  }
}

public class TestInvoice
{
  public void Test()
  {
    Invoice inv = new Invoice();
    inv.Items.Add(new Item("apple", 10));

    List my_items = new List();
    my_items.Add(new Item("apple", 10));

    inv.Items = my_items;   // compilation error here.
  }
}

+1, um hervorzuheben, dass Eigenschaften über den öffentlichen Getter bearbeitet werden können, obwohl sie einen privaten Setter haben.
user1725145

4

Angenommen, Sie speichern die tatsächliche Variable nicht über die Eigenschaft und verwenden den Wert nicht, um etwas zu berechnen.

In diesem Fall können Sie entweder eine Methode für Ihre Berechnung erstellen

private void Calculate(int value)
{
 //...
}

Oder Sie können dies mit tun

public int MyProperty {get; private set;}

In diesen Fällen würde ich empfehlen, das spätere zu verwenden, da die Eigenschaften jedes Elementelement intakt umgestalten.

Wenn Sie nicht einmal sagen, dass Sie die Eigenschaft einer Variablen zuordnen. In einem solchen Fall möchten Sie in Ihrem Code Folgendes schreiben:

public int myprop;
public int MyProperty {get { return myprop;}}

... ...

this.myprop = 30;

... ...
if(this.MyProperty > 5)
   this.myprop = 40;

Der obige Code sieht schrecklich aus, da der Programmierer immer vorsichtig sein muss, um MyProperty for Get und myprop for Set zu verwenden.

Aus Gründen der Konsistenz können Sie einen privaten Setter verwenden, der die Eigenschaft schreibgeschützt macht, während Sie den Setter innerhalb Ihres Codes verwenden können.


3

Ich denke, ein paar Leute haben darüber getanzt, aber für mich ist der Wert privater Setter, dass Sie das Verhalten einer Eigenschaft selbst innerhalb einer Klasse zusammenfassen können. Wie abhishek bemerkte, müssen Sie entweder einen privaten Setter verwenden oder das Ereignis auslösen, wenn Sie jedes Mal, wenn sich eine Eigenschaft ändert, ein Ereignis mit geänderter Eigenschaft auslösen möchten, aber nicht möchten, dass eine Eigenschaft öffentlich gelesen / geschrieben wird Ereignis überall dort, wo Sie das Hintergrundfeld ändern. Letzteres ist fehleranfällig, da Sie es möglicherweise vergessen. Wenn das Aktualisieren eines Eigenschaftswerts dazu führt, dass eine Berechnung durchgeführt oder ein anderes Feld geändert wird, oder eine verzögerte Initialisierung von etwas, sollten Sie dies auch im privaten Setter zusammenfassen, anstatt daran denken zu müssen, dies überall zu tun, wo Sie es tun Verwendung des Hintergrundfeldes.


2

Kapselung bedeutet, dass der Status eines Objekts nur über eine definierte Schnittstelle erfolgt. Aus diesem Grund kann die Klasse sicherstellen, dass dieser Status immer gültig ist und dem Zweck der Klasse entspricht.

In einigen Fällen entspricht es daher vollkommen dem Prinzip der Kapselung, ein Feld nur öffentlich zugänglich zu machen. Alle möglichen Werte für das Feld gelten für alle anderen möglichen Werte aller anderen Felder, und daher kann der Programmierer aktiv entscheiden, das Feld zuzulassen durch externen Code frei manipuliert werden.

Diese Fälle sind jedoch meist auf Klassen beschränkt, bei denen es sich meist um "einfache alte Daten" handelt. Sie sind auch in dieser Hinsicht nicht sehr interessant, also genug über sie.

In anderen Fällen, in anderen Sprachen, hätte man eine Getter- und Setter-Methode, so etwas wie int getId()einen Wert zu erhalten undvoid setId(int val) zu aktualisieren.

Mit den Eigenschaften können wir dieselbe Syntax zum Lesen und Schreiben mithilfe solcher Methoden verwenden, die wir zum Lesen und Schreiben eines Felds verwenden würden. Dies ist ein guter syntaktischer Zucker, wenn auch nicht lebenswichtig.

(Eigentlich aufgrund der Art und Weise, wie Reflexion funktioniert und Fälle wie DataBinder.Eval es praktisch sein kann, eine Eigenschaft zu haben, selbst wenn ein Feld gut funktionieren würde, aber das ist eine andere Sache).

Bis zur Einführung privater Setter (was sich mit C # 2 tatsächlich geändert hat, ist die Syntax, einen privaten Setter und einen öffentlichen oder geschützten Getter im selben Block zu haben), könnten wir eine private Methode haben, um die Arbeit des privaten Setters zu erledigen Private Setter sind nicht wirklich notwendig. Sie sind zwar praktisch, aber nur syntaktischer Zucker, aber ziemlich nützlich.

Bei der Kapselung geht es nicht darum, ob Ihre Setter (oder Getter) öffentlich, privat, geschützt oder intern sind, sondern darum, ob sie angemessen sind . Beginnen Sie mit der Standardeinstellung, dass jedes Feld privat ist (und dies auch readonly), und fügen Sie bei Bedarf Mitglieder (ob Eigenschaften oder Methoden) hinzu, die diese Felder ändern, und stellen Sie sicher, dass das Objekt bei Änderungen gültig bleibt . Dies stellt sicher, dass die Invariante einer Klasse beibehalten wird, was bedeutet, dass die Regeln, die den gültigen Satz von Zuständen beschreiben, in denen sie sich befinden kann, niemals verletzt werden (Konstruktoren helfen auch, indem sie sicherstellen, dass sie in einem solchen gültigen Zustand beginnen).

Unveränderlich zu sein bedeutet für Ihre letzte Frage, dass eine Klasse keine öffentlichen, geschützten oder internen Setter und keine öffentlichen, geschützten oder internen Methoden hat, die Felder ändern. Es gibt Grade davon, in C # sind drei Grade möglich:

  1. Alle Instanzfelder einer Klasse sind readonly, daher kann auch privater Code sie nicht ändern. Es ist garantiert unveränderlich (alles, was versucht, es zu ändern, wird nicht kompiliert), und möglicherweise können Optimierungen auf dieser Rückseite vorgenommen werden.

  2. Eine Klasse ist von außen unveränderlich, weil kein öffentliches Mitglied etwas ändert, aber nicht garantiert wird, dass readonlyes nicht von innen geändert wird.

  3. Eine Klasse ist von außen gesehen unveränderlich, obwohl sich ein Zustand als Implementierungsdetail ändert. Zum Beispiel könnte ein Feld gespeichert werden, und während von außen versucht wird, es nur den gleichen Wert abzurufen, berechnet der erste derartige Versuch es tatsächlich und speichert es dann zum Abrufen bei nachfolgenden Versuchen.


1

Sie benötigen einen privaten Setter, wenn Sie das folgende Szenario unterstützen möchten (nicht nur aus diesem Grund, sondern auch aus einem guten Grund): Sie haben eine Eigenschaft, die in Ihrer Klasse schreibgeschützt ist, dh nur die Klasse selbst darf sich ändern es, aber es kann es nach dem Erstellen der Instanz ändern. Für Bindungen müssten Sie dann ein PropertyChanged-Ereignis auslösen, dies sollte vorzugsweise im (privaten) Eigenschaftssetter erfolgen. Eigentlich könnten Sie das PropertyChanged-Ereignis einfach von einer anderen Stelle in der Klasse auslösen, aber die Verwendung des privaten Setters ist "gute Staatsbürgerschaft", da Sie Ihre Eigenschaftsänderungsauslöser nicht in Ihrer gesamten Klasse verteilen, sondern bei der Eigentum, wo es hingehört.


1

Ja, Sie verwenden die Kapselung mithilfe von Eigenschaften, aber die Kapselung enthält mehr Nuancen als nur die Kontrolle darüber, wie Eigenschaften gelesen und geschrieben werden. Das Ablehnen einer Eigenschaft, die von außerhalb der Klasse festgelegt werden soll, kann sowohl für die Robustheit als auch für die Leistung nützlich sein.

Eine unveränderliche Klasse ist eine Klasse, die sich nach ihrer Erstellung nicht ändert. Daher sind private Setter (oder überhaupt keine Setter) erforderlich, um die Eigenschaften zu schützen.

Private Setter wurden häufiger mit der in C # 3 eingeführten Eigenschaftskürzel verwendet. In C # 2 wurde der Setter häufig einfach weggelassen, und auf die privaten Daten wurde beim Setzen direkt zugegriffen.

Diese Liegenschaft:

public int Size { get; private set; }

ist das gleiche wie:

private int _size;
public int Size {
  get { return _size; }
  private set { _size = value; }
}

Der Name der Sicherungsvariablen wird jedoch intern vom Compiler erstellt, sodass Sie nicht direkt darauf zugreifen können.

Bei der Shorthand-Eigenschaft wird der private Setter benötigt, um eine schreibgeschützte Eigenschaft zu erstellen, da Sie nicht direkt auf die Hintergrundvariable zugreifen können.


Wenn ich mich richtig erinnere, konnten Sie auf getund setvor C # 2.0 keine unterschiedlichen Zugriffsmodifikatoren haben . Ich denke auch, dass Sie 2.0 und 3.0 mischen, da die automatisch implementierte Kurzform, auf die Sie sich beziehen, 3.0 war.
Anthony Pegram

1

Ich verstehe nicht die Notwendigkeit privater Setter, die mit C # 2 begonnen haben.

Anwendungsfallbeispiel:

Ich habe eine Instanz eines Anwendungsobjekts 'UserInfo', das eine Eigenschaft enthält SessionTokenIDV1, die ich Verbrauchern meiner Klasse nicht zur Verfügung stellen möchte.

Ich brauche auch die Fähigkeit, diesen Wert aus meiner Klasse festzulegen.

Meine Lösung bestand darin, die Eigenschaft wie gezeigt zu kapseln und den Setter privat zu machen, damit ich den Wert des Sitzungstokens festlegen kann, ohne dass der Instanziierungscode ihn auch festlegen kann (oder in meinem Fall sogar sehen kann).

public class UserInfo
{
   public String SessionTokenIDV1 { get; set; }

}


public class Example
{
  // Private vars
  private UserInfo _userInfo = new UserInfo();

  public string SessionValidV1
  {
    get { return ((_userInfo.SessionTokenIDV1 != null) && (_userInfo.SessionTokenIDV1.Length > 0)) ? "set" : "unset"; }
    private set { _userInfo.SessionTokenIDV1 = value; }
  }
}

Bearbeiten: Behobenes Code-Tag Bearbeiten: Beispiel hatte Fehler, die korrigiert wurden


-1

Credits an https://www.dotnetperls.com/property .

Private Setter sind mit schreibgeschützten Feldern identisch. Sie können nur im Konstruktor festgelegt werden. Wenn Sie versuchen, von außerhalb einzustellen, wird ein Fehler bei der Kompilierung angezeigt.

public class MyClass
{
    public MyClass()
    {
        // Set the private property.
        this.Name = "Sample Name from Inside";
    }
     public MyClass(string name)
    {
        // Set the private property.
        this.Name = name;
    }
    string _name;
    public string Name
    {
        get
        {
            return this._name;
        }
        private set
        {
            // Can only be called in this class.
            this._name = value;
        }
    }
}

class Program
{
    static void Main()
    {
        MyClass mc = new MyClass();
        Console.WriteLine(mc.name);

        MyClass mc2 = new MyClass("Sample Name from Outside");
        Console.WriteLine(mc2.name);
    }
}

Bitte sehen Sie den folgenden Screenshot, als ich versuchte, ihn von außerhalb der Klasse einzustellen.

Geben Sie hier die Bildbeschreibung ein

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.