Warum wird nicht empfohlen, nur Set-Eigenschaften zu haben?

9

Heute bei der Arbeit hat einer meiner Kollegen meinen Code überprüft und mir vorgeschlagen, eine Nur-Set-Eigenschaft zu entfernen und stattdessen eine Methode zu verwenden.

Da wir beide mit anderen Dingen beschäftigt waren, sagte er mir, ich solle mir den Property DesignAbschnitt aus dem Buch "Framework Design Guidelines" ansehen . In dem Buch sagte der Autor nur, um zu vermeiden:

Eigenschaften, bei denen der Setter eine breitere Zugänglichkeit als der Getter hat

Und jetzt frage ich mich, warum es nicht empfohlen wird, nur Set-Eigenschaften zu haben? Kann mir jemand das erklären?

Prashant Cholachagudda
quelle
6
Können Sie die Situation beschreiben, in der Sie eine Nur-Set-Eigenschaft für angemessen hielten? Dies könnte die Antworten etwas relevanter machen.
JohnFx
1
Ich versuche mir ein Beispiel auszudenken, das semantisch Sinn macht. Das einzige, was mir in den Sinn kommt, ist eine PasswordEigenschaft einer UserKlasse. Sie können es einstellen, aber Sie können es nicht bekommen. Sie könnten dann eine schreibgeschützte HashedPasswordEigenschaft haben. Das Aufrufen des Sets würde den Hash ausführen und die HashedPasswordEigenschaft ändern . Ich würde dich nicht anschreien, wenn du das tust.
Scott Whitlock

Antworten:

15

Ich denke, das hat vielleicht mit den Erwartungen zu tun. Nur-Set-Eigenschaften sind ungewöhnlich und Eigenschaften werden normalerweise für "dumme" Sets verwendet, um einen Wert ohne viel Verarbeitung zu speichern. Wenn Sie viel in einem Setter arbeiten, ist es besser, eine Methode zu verwenden. Die Leute erwarten, dass die Ausführung von Methoden möglicherweise lange dauert und möglicherweise Nebenwirkungen hat. Das Implementieren eines ähnlichen Verhaltens in einer Eigenschaft kann zu Code führen, der die Erwartungen verletzt.

Hier ist ein relevanter Abschnitt der Microsoft-Richtlinien zur Verwendung von Eigenschaften :

Eigenschaften vs. Methoden

Klassenbibliotheksdesigner müssen sich häufig zwischen der Implementierung eines Klassenmitglieds als Eigenschaft oder Methode entscheiden. Im Allgemeinen repräsentieren Methoden Aktionen und Eigenschaften repräsentieren Daten. Verwenden Sie die folgenden Richtlinien, um zwischen diesen Optionen zu wählen.

  • Verwenden Sie eine Eigenschaft, wenn das Mitglied ein logisches Datenelement ist. In den folgenden Elementdeklarationen Namehandelt es sich um eine Eigenschaft, da es sich um ein logisches Mitglied der Klasse handelt.
public string Name
{
    get 
    {
        return name;
    }
    set 
    {
        name = value;
    }
}

Verwenden Sie eine Methode, wenn:

  • Die Operation ist eine Konvertierung, wie z Object.ToString.
  • Der Vorgang ist so teuer, dass Sie dem Benutzer mitteilen möchten, dass er das Ergebnis zwischenspeichern sollte.
  • Das Erhalten eines Eigenschaftswerts mit dem getAccessor hätte einen beobachtbaren Nebeneffekt.
  • Wenn Sie das Mitglied zweimal hintereinander anrufen, werden unterschiedliche Ergebnisse erzielt.
  • Die Reihenfolge der Ausführung ist wichtig. Beachten Sie, dass die Eigenschaften eines Typs in beliebiger Reihenfolge festgelegt und abgerufen werden können sollten.
  • Das Mitglied ist statisch, gibt jedoch einen Wert zurück, der geändert werden kann.
  • Das Mitglied gibt ein Array zurück. Eigenschaften, die Arrays zurückgeben, können sehr irreführend sein. Normalerweise muss eine Kopie des internen Arrays zurückgegeben werden, damit der Benutzer den internen Status nicht ändern kann. Dies führt zusammen mit der Tatsache, dass ein Benutzer leicht annehmen kann, dass es sich um eine indizierte Eigenschaft handelt, zu ineffizientem Code. Im folgenden Codebeispiel erstellt jeder Aufruf der Methods-Eigenschaft eine Kopie des Arrays. Infolgedessen werden in der folgenden Schleife 2 ^ n + 1 Kopien des Arrays erstellt.
Type type = // Get a type.
for (int i = 0; i < type.Methods.Length; i++)
{
   if (type.Methods[i].Name.Equals ("text"))
   {
      // Perform some operation.
   }
}

[... längeres Beispiel übersprungen ...]

Schreibgeschützte und schreibgeschützte Eigenschaften

Sie sollten eine schreibgeschützte Eigenschaft verwenden, wenn der Benutzer das logische Datenelement der Eigenschaft nicht ändern kann. Verwenden Sie keine schreibgeschützten Eigenschaften.

Adam Lear
quelle
Ja - hier gilt das Prinzip der geringsten Überraschung.
Paul Butcher
6

Weil es in den meisten Fällen einfach keinen Sinn macht. Welche Eigenschaft könnten Sie haben, die Sie festlegen, aber nicht lesen können?

Wenn OO die reale Welt besser darstellen soll, deutet eine Eigenschaft nur auf eine Menge wahrscheinlich darauf hin, dass Ihre Modellierung ziemlich schlecht ist.

Bearbeiten : Siehe auch: /programming/4564928/are-set-only-properties-bad-practice, das im Wesentlichen besagt, dass es nicht intuitiv ist und eine Eigenschaft nur für Sets im Grunde eine Methode mit einem anderen Namen ist, daher sollten Sie eine verwenden Methode.

Jon Hopkins
quelle
1
Ich habe zuvor nur Set-Eigenschaften verwendet. Sie schreiben in ein privates Feld des Objekts, um dessen Verhalten zu konfigurieren. Sie sind nützlich, wenn externer Code den aktuellen Wert nicht kennen muss, ihn aber möglicherweise ändern muss. Das ist natürlich selten, aber ich habe es gesehen.
Mason Wheeler
@Mason - Ich würde sicherlich nie so weit gehen zu sagen, dass Sie sie niemals verwenden sollten, aber sie sollten im Wesentlichen eher die Ausnahme als die Regel sein.
Jon Hopkins
@ MasonWheeler ist das nicht ungefähr Foo Foo { private get; set; }? Ich würde das nicht nur Schreiben nennen
Caleth
6

Nun, ich stelle mir vor, dass Sie, wenn Sie eine Eigenschaft für etwas festlegen können, diese aber nie erhalten, nie wissen, ob etwas anderes den von Ihnen festgelegten Wert ändert / überschreibt. Dies kann ein Problem sein, wenn Sie sich auf den von Ihnen festgelegten Wert verlassen und (aus irgendeinem Grund) nicht in der Lage sind, ihn bis zu dem Zeitpunkt beizubehalten, an dem Sie ihn erhalten möchten.

Die Verwendung einer Methode anstelle einer Nur-Set-Eigenschaft ist für einen Benutzer etwas weniger verwirrend. Der Name der Methode zeigt in der Regel Set- oder get- , aber Eigenschaftsnamen angeben , normalerweise nicht , dass etwas kann nur und eingestellt wird nicht geholt werden. Ich nehme an, wenn die Eigenschaft so etwas wie "ReadOnlyBackgroundColour" wäre, wäre es für andere Codierer nicht verwirrend, aber das würde einfach komisch aussehen.

FrustratedWithFormsDesigner
quelle
Ich stimme zu, aber wie würde sich eine Setter-Methode in diesem Fall unterscheiden?
Travis Christian
1
@Travis Christian: Es hört sich so an, als würde OP mit einer Situation arbeiten, in der es einen Setter gibt, aber keinen Getter. Sie können also etwas einstellen, aber sie werden nie erfahren, ob es sich später ändert.
FrustratedWithFormsDesigner
@Frustrated Aber sie würden auch nie erfahren, ob mit einer Methode wieder etwas geändert wurde.
Adam Lear
@Anna Lear ♦: Wenn es einen Getter gibt, können Sie den Wert zumindest testen, bevor Sie ihn verwenden, wenn Sie einen Punkt in Ihrem Code haben, an dem der von Ihnen festgelegte Wert plötzlich zweifelhaft sein kann.
FrustratedWithFormsDesigner
3
@Frustrated stimme ich zu. Es ist nur so, dass es um die Verwendung einer Nur-Set-Eigenschaft im Vergleich zur Verwendung einer Methode ging, um dasselbe zu tun.
Adam Lear
-1

Dies ist ein sehr altes Thema, aber eines, das mir zu diesem späten Zeitpunkt in den Sinn gekommen ist, und ich hätte gerne einige Kommentare, wenn ich versuche, für schreibgeschützte Eigenschaften einzutreten ...

Ich habe eine Reihe von ActiveReportKlassen, die Teil einer Website sind, die instanziiert und nach einer Reihe von Benutzerauswahlen beim Postback ausgeführt werden.

Der VB-Code sieht ungefähr so ​​aus:

  Public Class SomeReport
    Private greader As New GenericReporting.CommonReader("AStoredProcedure", 
                                      {New SqlParameter("budget_id", 0)})

    Public WriteOnly Property BudgetID As Integer
      Set(value As Integer)
        greader.Parameters("budget_id").Value = value
      End Set
    End Property

    Public Sub New(Optional budget_id As Integer = 0)
      ' This call is required by the designer.
      InitializeComponent()

      ' Add any initialization after the InitializeComponent() call.
      BudgetID = budget_id
    End Sub
  End Class

Diese Berichte verwenden generische Eingeweide, verwenden CommonReadereine gespeicherte Prozedur und ein Array von Standardwerten SqlParameter, denen jeweils eine WriteOnly-Eigenschaft zugeordnet ist, die je nach Berichtsdesign als Parameter bei der Instanziierung übergeben oder vom Benutzer nach der vorherigen Instanziierung festgelegt werden kann Aufruf der Berichtsmethode Run.

  '''''''''''''''''''''''
  ' Parameter taken from a user selected row of a GridView
  '
  Dim SomeBudgetID As Integer = gvBudgets.SelectedDataKey.Values(budget_id)

  '''''''''''''''''''''''      
  ' On Instantiation
  '
  Dim R as ActiveReport = New SomeReport(SomeBudgetID)
  R.Run()

  '''''''''''''''''''''''      
  ' Or On Instantiation using "With" syntax
  '
  Dim R as ActiveReport = New SomeReport() With {.BudgetID = SomeBudgetID}
  R.Run()

  '''''''''''''''''''''''
  ' Or After
  '
  Dim R as ActiveReport = New SomeReport()
  R.BudgetID = SomeBudgetID
  R.Run()

Also, wie ich es sehe, in diesem Fall nur schreibgeschützt

  1. Ermöglicht eine stärkere Typprüfung, da SqlParameters generisch sind
  2. Mehr Flexibilität beim Erstellen des Berichts. Der Bericht kann sofort instanziiert werden, wenn alle Parameter verfügbar sind oder später hinzugefügt werden, sobald sie verfügbar sind.
  3. Die Eigenschaften unterstützen die "With" -Syntax bei der Instanziierung
  4. Ist ein "Getter" wirklich notwendig, da die Parameter dem Benutzer bekannt sind und vom Bericht nicht geändert werden?
  5. Da es sich bei SqlParameters um Klassen und nicht um primitive Werte handelt, ermöglichen die WriteOnly-Eigenschaften eine einfachere Schnittstelle zum Festlegen von Parametern

Das sind also meine Gedanken.

Könnte ich es stattdessen in eine Methode konvertieren? sicher, aber die Schnittstelle scheint ... weniger schön

  R2.BudgetID = SomeBudgetID

gegen

  R2.SetBudgetID(SomeBudgetID)
fnostro
quelle