104 Stimmen

Warum sollte ich die Verwendung von Eigenschaften in C# vermeiden?

In seinem hervorragenden Buch CLR Via C# sagt Jeffrey Richter, dass er Eigenschaften nicht mag und empfiehlt, sie nicht zu verwenden. Er gab einige Gründe an, aber ich verstehe sie nicht wirklich. Kann mir jemand erklären, warum ich Eigenschaften verwenden sollte oder nicht? Ändert sich das in C# 3.0, mit automatischen Eigenschaften?

Als Referenz habe ich die Stellungnahmen von Jeffrey Richter hinzugefügt:

- Eine Eigenschaft kann schreibgeschützt oder schreibgeschützt sein; der Feldzugriff ist immer lesbar und schreibbar. Wenn Sie eine Eigenschaft definieren, ist es am besten, sowohl Get- als auch Set-Accessor-Methoden anzubieten.

- Eine Eigenschaftsmethode kann eine Ausnahme auslösen; ein Feldzugriff löst nie eine Ausnahme aus.

- Eine Eigenschaft kann nicht als out- oder ref-Parameter an eine Methode übergeben werden, ein Feld schon. Für Beispiel: Der folgende Code lässt sich nicht kompilieren:

using System;
public sealed class SomeType
{
   private static String Name 
   {
     get { return null; }
     set {}
   }
   static void MethodWithOutParam(out String n) { n = null; }
   public static void Main()
   {
      // For the line of code below, the C# compiler emits the following:
      // error CS0206: A property or indexer may not
      // be passed as an out or ref parameter
      MethodWithOutParam(out Name);
   }
}

- Die Ausführung einer Eigenschaftsmethode kann lange dauern; der Feldzugriff wird immer sofort abgeschlossen. Ein häufiger Grund für die Verwendung von Eigenschaften ist die Synchronisierung von Threads, die den Thread für immer anhalten kann. Daher sollte eine Eigenschaft nicht verwendet werden, wenn eine Thread-Synchronisierung erforderlich ist. In dieser Situation ist eine Methode vorzuziehen. Wenn auf Ihre Klasse auch aus der Ferne zugegriffen werden kann auf Ihre Klasse aus der Ferne zugegriffen werden kann (z. B. wenn Ihre Klasse von System.MashalByRefObject abgeleitet ist), wird der Aufruf der Eigenschaftsmethode sehr langsam sein, und daher ist eine Methode einer Eigenschaft. Meiner Meinung nach sollten von MarshalByRefObject abgeleitete Klassen niemals Eigenschaften verwenden.

- Wenn eine Eigenschaftsmethode mehrmals hintereinander aufgerufen wird, kann sie jedes Mal einen anderen Wert zurückgeben jedes Mal einen anderen Wert zurückgeben; ein Feld gibt jedes Mal denselben Wert zurück. Die Klasse System.DateTime hat eine schreibgeschützte Now-Eigenschaft, die das aktuelle Datum und die aktuelle Uhrzeit zurückgibt. Jedes Mal, wenn Sie diese Eigenschaft abfragen, wird ein anderer Wert zurückgegeben. Dies ist ein Fehler, und Microsoft wünscht, dass sie die Klasse reparieren könnte, indem sie Now zu einer Methode statt zu einer Eigenschaft macht.

- Eine Eigenschaftsmethode kann beobachtbare Nebeneffekte haben, ein Feldzugriff hat das nie. In anderen Ein Benutzer eines Typs sollte also in der Lage sein, verschiedene von einem Typ definierte Eigenschaften in beliebiger Reihenfolge zu setzen, ohne ein anderes Verhalten des Typs zu bemerken.

- Eine Eigenschaftsmethode kann zusätzlichen Speicher benötigen oder einen Verweis auf etwas zurückgeben zurück, das eigentlich nicht zum Zustand des Objekts gehört, so dass eine Änderung des zurückgegebenen Objekts keine Auswirkungen auf das ursprüngliche Objekt; die Abfrage eines Feldes gibt immer einen Verweis auf ein Objekt zurück, das garantiert Teil des Zustands des ursprünglichen Objekts ist. Die Arbeit mit einer Eigenschaft, die eine Kopie zurückgibt, kann für Entwickler sehr verwirrend sein, und diese Eigenschaft ist häufig nicht dokumentiert.

11 Stimmen

Ich besitze die 3. Auflage von "CLR via C#" und auf Seite 242 sagt Herr Richter, dass er persönlich keine Eigenschaften mag, aber er empfiehlt nie, sie nicht zu verwenden. Bitte geben Sie die Buchversion und die Seitenzahl an, wo Sie dies gelesen haben.

176voto

Jon Skeet Punkte 1325502

Jeffs Grund für die Abneigung gegen Eigenschaften ist, dass sie wie Felder aussehen. Entwickler, die den Unterschied nicht verstehen, behandeln sie wie Felder und gehen davon aus, dass sie billig in der Ausführung sind usw.

Ich persönlich stimme in diesem Punkt nicht mit ihm überein - ich finde, dass Eigenschaften den Client-Code viel einfacher lesbar machen als die entsprechenden Methodenaufrufe. Ich stimme zu, dass Entwickler wissen müssen, dass Eigenschaften im Grunde getarnte Methoden sind - aber ich denke, dass es besser ist, die Entwickler darüber aufzuklären, als den Code mit Methoden schwerer lesbar zu machen. (Insbesondere weiß ich, nachdem ich Java-Code mit mehreren Gettern und Settern gesehen habe, die in derselben Anweisung aufgerufen werden, dass der entsprechende C#-Code viel einfacher zu lesen wäre. Das Gesetz von Demeter ist in der Theorie schön und gut, aber manchmal foo.Name.Length wirklich das Richtige ist...)

(Und nein, die automatisch implementierten Eigenschaften ändern daran nichts).

Dies ist ein wenig wie die Argumente gegen die Verwendung von Erweiterungsmethoden - ich kann die Argumentation nachvollziehen, aber der praktische Nutzen (bei sparsamer Verwendung) überwiegt meiner Meinung nach den Nachteil.

34voto

Hejazzman Punkte 1995

Nun, gehen wir seine Argumente der Reihe nach durch:

Eine Eigenschaft kann schreibgeschützt sein oder schreibgeschützt sein; der Feldzugriff ist immer lesbar und beschreibbar.

Dies ist ein Gewinn für die Eigenschaften, da Sie den Zugriff genauer steuern können.

Eine Eigenschaftsmethode kann eine Ausnahme auslösen; ein Feldzugriff löst nie eine Ausnahme.

Dies ist zwar größtenteils richtig, aber Sie können sehr wohl eine Methode für ein nicht initialisiertes Objektfeld aufrufen und eine Ausnahme auslösen lassen.

- Eine Eigenschaft kann nicht als out- oder ref-Parameter an eine Methode übergeben werden; ein Feld kann.

Schön.

- Eine Eigenschaftsmethode kann lange dauern Zeit in Anspruch nehmen; ein Feldzugriff wird immer sofort abgeschlossen werden.

Es kann auch sehr wenig Zeit in Anspruch nehmen.

- Bei mehreren aufeinanderfolgenden Aufrufen wird ein Eigenschaftsmethode jedes Mal einen anderen Wert zurückgeben; ein Feld gibt jedes Mal den Feld gibt jedes Mal denselben Wert zurück.

Das stimmt nicht. Woher wissen Sie, dass der Wert des Feldes nicht geändert wurde (möglicherweise durch einen anderen Thread)?

Die Klasse System.DateTime hat eine schreibgeschützte Now-Eigenschaft, die das das aktuelle Datum und die Uhrzeit zurückgibt. Jedes Mal, wenn Sie diese Eigenschaft abfragen, gibt sie einen anderen Wert zurück. Dies ist ein Fehler, und Microsoft wünschte, sie könnten die Klasse reparieren, indem sie Now zu einer Methode statt einer Eigenschaft macht.

Wenn es ein Fehler ist, dann ist es ein kleiner.

- Eine Eigenschaftsmethode kann dazu führen beobachtbare Seiteneffekte verursachen; der Feldzugriff tut dies nie. Mit anderen Worten, ein Benutzer von eines Typs sollte in der Lage sein, verschiedene Eigenschaften, die durch einen Typ definiert sind, in beliebiger Reihenfolge zu setzen, ohne dass er ein unterschiedliches Verhalten des Typ zu bemerken.

Schön.

- Eine Eigenschaftsmethode kann Folgendes erfordern zusätzlichen Speicher benötigen oder einen Referenz auf etwas zurück, das nicht nicht zum Zustand des Objekts gehört, daher hat die Änderung des zurückgegebenen Objekts keine Auswirkung auf das ursprüngliche Objekt; die Abfrage eines Feldes gibt immer einen Referenz auf ein Objekt zurück, das garantiert ein Teil des ursprünglichen Objekts ist. Die Arbeit mit einer Eigenschaft, die eine Kopie zurückgibt, kann für Entwickler sehr verwirrend sein, und diese Eigenschaft ist häufig nicht dokumentiert.

Die meisten dieser Behauptungen könnten auch für Javas Getter und Setter gelten - und wir hatten sie eine ganze Weile lang ohne solche Probleme in der Praxis.

Ich denke, die meisten Probleme könnten durch eine bessere Syntaxhervorhebung (d.h. Unterscheidung von Eigenschaften und Feldern) gelöst werden, damit der Programmierer weiß, was er zu erwarten hat.

19voto

jalf Punkte 235501

Ich habe das Buch nicht gelesen, und Sie haben den Teil, den Sie nicht verstehen, nicht zitiert, also werde ich raten müssen.

Manche Leute mögen Eigenschaften nicht, weil sie Ihren Code zu überraschenden Dingen veranlassen können.

Wenn ich tippe Foo.Bar wird man normalerweise erwarten, dass es sich um einen einfachen Zugriff auf ein Mitgliedsfeld der Klasse Foo handelt. Es ist eine billige, fast kostenlose Operation, und sie ist deterministisch. Ich kann sie immer wieder aufrufen und erhalte jedes Mal das gleiche Ergebnis.

Bei Eigenschaften kann es sich stattdessen um einen Funktionsaufruf handeln. Es könnte eine Endlosschleife sein. Sie könnte eine Datenbankverbindung öffnen. Jedes Mal, wenn ich darauf zugreife, könnte sie andere Werte zurückgeben.

Es ist ein ähnliches Argument wie das, warum Linus C++ hasst. Ihr Code kann für den Leser überraschend wirken. Er hasst das Überladen von Operatoren: a + b bedeutet nicht unbedingt eine einfache Addition. Es kann eine äußerst komplizierte Operation bedeuten, genau wie C#-Eigenschaften. Sie kann Seiteneffekte haben. Sie kann alles Mögliche tun.

Ehrlich gesagt, halte ich das für ein schwaches Argument. Beide Sprachen sind voll von derartigen Dingen. (Sollten wir die Überladung von Operatoren in C# ebenfalls vermeiden? Immerhin kann dort das gleiche Argument verwendet werden)

Eigenschaften ermöglichen Abstraktion. Wir können so tun, als ob dass es sich um ein reguläres Feld handelt, und es so verwenden, als ob es eines wäre, und sich nicht darum kümmern müssen, was hinter den Kulissen passiert.

Das ist normalerweise eine gute Sache, aber es hängt natürlich davon ab, dass der Programmierer sinnvolle Abstraktionen schreibt. Ihre Eigenschaften sollte verhalten sich wie Felder. Sie sollten keine Seiteneffekte haben und keine teuren oder unsicheren Operationen durchführen. Wir wollen in der Lage sein an sie denken als Felder.

Ich habe jedoch einen weiteren Grund, warum ich sie nicht perfekt finde. Sie können nicht per Referenz an andere Funktionen übergeben werden.

Felder können übergeben werden als ref und ermöglicht einer aufgerufenen Funktion den direkten Zugriff darauf. Funktionen können als Delegierte übergeben werden, so dass eine aufgerufene Funktion direkt auf sie zugreifen kann.

Eigenschaften... können es nicht.

Das nervt.

Das heißt aber nicht, dass Eigenschaften schlecht sind oder nicht genutzt werden sollten. Für viele Zwecke sind sie großartig.

19voto

Aaronaught Punkte 118136

Damals, im Jahr 2009, schien dieser Ratschlag nur ein Gejammer der Who Moved My Cheese Sorte. Heute ist sie fast lächerlich veraltet.

Ein sehr wichtiger Punkt, um den viele Antworten nur herumzuschleichen scheinen, ohne ihn wirklich anzusprechen, ist, dass diese angeblichen "Gefahren" von Immobilien folgende sind ein bewusster Teil der Rahmengestaltung!

Ja, Immobilien können das:

  • Geben Sie unterschiedliche Zugriffsmodifikatoren für den Getter und Setter an. Dies ist ein Vorteil über Felder. Ein gängiges Muster ist, einen öffentlichen Getter und einen geschützt o intern Setter, eine sehr nützliche Vererbungstechnik, die mit Feldern allein nicht zu erreichen ist.

  • Eine Ausnahme auslösen. Bis heute ist dies eine der effektivsten Methoden der Validierung, insbesondere bei der Arbeit mit UI-Frameworks, die Datenbindungskonzepte beinhalten. Bei der Arbeit mit Feldern ist es viel schwieriger, sicherzustellen, dass ein Objekt in einem gültigen Zustand bleibt.

  • Die Ausführung dauert sehr lange. Der gültige Vergleich ist hier mit Methoden die ebenso lange dauern - nicht Felder . Für die Aussage "eine Methode wird bevorzugt" gibt es keine andere Grundlage als die persönliche Vorliebe eines Autors.

  • Bei späteren Ausführungen andere Werte von seinem Getter zurückgeben. Dies erscheint fast wie ein Witz, wenn man bedenkt, dass die Tugenden von ref / out Parameter mit Feldern, deren Wert eines Feldes nach einem ref / out ist so gut wie sicher, dass er sich von seinem vorherigen Wert unterscheidet, und zwar auf unvorhersehbare Weise.

    Wenn wir über den speziellen (und praktisch akademischen) Fall eines einfädigen Zugangs ohne afferente Kopplungen sprechen, ist es ziemlich gut verstanden dass es einfach schlechtes Eigenschaftsdesign ist, sichtbare zustandsverändernde Nebeneffekte zu haben, und vielleicht verblasst mein Gedächtnis, aber ich kann mich einfach nicht an Beispiele erinnern, in denen Leute DateTime.Now und erwarten, dass jedes Mal derselbe Wert herauskommt. Zumindest keine Fälle, in denen sie es mit einem hypothetischen Wert nicht genauso vermasselt hätten. DateTime.Now() .

  • Sie verursachen beobachtbare Nebeneffekte - was natürlich genau der Grund ist, warum Eigenschaften als Sprachmerkmal überhaupt erst erfunden wurden. Microsofts eigene Grundstücksgestaltung Die Leitlinien besagen, dass die Reihenfolge der Setzer keine Rolle spielen sollte, da eine andere Vorgehensweise bedeuten würde, dass zeitliche Kopplung . Sicherlich kann man mit Feldern allein keine zeitliche Kopplung erreichen, aber das liegt nur daran, dass man mit Feldern allein überhaupt kein sinnvolles Verhalten auslösen kann, bis eine Methode ausgeführt wird.

    Property Accessors können tatsächlich helfen verhindern. bestimmte Arten der zeitlichen Kopplung, indem das Objekt in einen gültigen Zustand gezwungen wird, bevor irgendeine Aktion ausgeführt wird - zum Beispiel, wenn eine Klasse eine StartDate und ein EndDate und dann die Einstellung der EndDate vor dem StartDate könnte die StartDate auch zurück. Dies gilt selbst in Multithreading- oder asynchronen Umgebungen, einschließlich des offensichtlichen Beispiels einer ereignisgesteuerten Benutzeroberfläche.

Andere Dinge, die Eigenschaften tun können, die Felder nicht können, sind:

  • Langsames Laden eine der wirksamsten Methoden zur Vermeidung von Fehlern bei der Initialisierungsreihenfolge.
  • Benachrichtigungen ändern die so ziemlich die gesamte Grundlage für die MVVM Architektur.
  • Vererbung zum Beispiel durch die Definition einer abstrakten Type o Name So können abgeleitete Klassen interessante, aber dennoch konstante Metadaten über sich selbst liefern.
  • Abfangen dank der obigen Ausführungen.
  • Indexer die jeder kennt, der schon einmal mit COM interop und dem unvermeidlichen Aufkommen von Item(i) Anrufe als eine wunderbare Sache erkennen werden.
  • Arbeit mit PropertyDescriptor die für die Erstellung von Designern und für XAML-Frameworks im Allgemeinen unerlässlich ist.

Richter ist eindeutig ein produktiver Autor und weiß eine Menge über die CLR und C#, aber ich muss sagen, es scheint, als wenn er diesen Rat ursprünglich schrieb (ich bin nicht sicher, ob es in seinen neueren Überarbeitungen ist - ich hoffe aufrichtig nicht), dass er einfach nicht von alten Gewohnheiten loslassen wollte und Schwierigkeiten hatte, die Konventionen von C# (vs. C++, zum Beispiel) zu akzeptieren.

Was ich damit meine, ist, dass sein Argument der "als schädlich angesehenen Eigenschaften" im Wesentlichen auf eine einzige Aussage hinausläuft: Eigenschaften sehen wie Felder aus, verhalten sich aber möglicherweise nicht wie Felder. Und das Problem bei dieser Aussage ist, es ist nicht wahr oder bestenfalls höchst irreführend ist. Eigenschaften nicht sehen aus wie Felder - zumindest sind sie es nicht angeblich die wie Felder aussehen.

Es gibt zwei très starke Codierungskonventionen in C# mit ähnlichen Konventionen, die von anderen CLR-Sprachen geteilt werden, und FXCop wird Sie anschreien, wenn Sie diese nicht befolgen:

  1. Felder sollten immer privat sein, niemals öffentlich.
  2. Felder sollten in camelCase deklariert werden. Eigenschaften sind in PascalCase.

Es besteht also keine Unklarheit darüber, ob Foo.Bar = 42 ist ein Eigenschaftszugriff oder ein Feldzugriff. Es ist ein Eigenschaftszugriff und sollte wie jede andere Methode behandelt werden - es könnte langsam sein, eine Ausnahme auslösen usw. Das liegt in der Natur von Abstraktion - Es liegt ganz im Ermessen der anmeldenden Klasse, wie sie reagiert. Klassendesigner sollten das Prinzip der geringsten Überraschung anwenden, aber Aufrufer sollten nichts über eine Eigenschaft annehmen, außer dass sie das tut, was auf der Verpackung steht. Das ist gewollt.

Die Alternative zu Eigenschaften sind überall Getter/Setter-Methoden. Das ist der Java-Ansatz, und er hat sich von Anfang an umstritten . Es ist in Ordnung, wenn das Ihr Ding ist, aber das ist einfach nicht die Art, wie wir im .NET-Lager vorgehen. Wir versuchen, zumindest innerhalb der Grenzen eines statisch getypten Systems, das zu vermeiden, was Fowler als Syntaktisches Rauschen . Wir wollen keine zusätzlichen Klammern, zusätzliche get / set Warzen oder zusätzliche Methodensignaturen - nicht, wenn wir sie ohne Verlust an Klarheit vermeiden können.

Sagen Sie, was Sie wollen, aber foo.Bar.Baz = quux.Answers[42] wird immer viel einfacher zu lesen sein als foo.getBar().setBaz(quux.getAnswers().getItem(42)) . Und wenn man täglich Tausende von Zeilen davon liest, macht das schon einen Unterschied.

(Und wenn Ihre natürliche Reaktion auf den obigen Absatz ist, zu sagen: "Sicher, es ist schwer zu lesen, aber es wäre einfacher, wenn Sie es in mehrere Zeilen aufteilen würden", dann muss ich leider sagen, dass Sie <em>vollständig </em>das Thema verfehlt).

10voto

Konstantin Tarkus Punkte 36184

Ich sehe keinen Grund, warum man Eigenschaften nicht generell verwenden sollte.

Automatische Eigenschaften in C# 3+ vereinfachen die Syntax nur ein wenig (a la Syntatic Sugar).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X