In seinem ausgezeichneten Buch CLR Via C # sagte Jeffrey Richter, dass er Immobilien nicht mag und empfiehlt, sie nicht zu verwenden. Er gab einen Grund an, aber ich verstehe nicht wirklich. Kann mir jemand erklären, warum ich Eigenschaften verwenden sollte oder nicht? Ändert sich dies in C # 3.0 mit automatischen Eigenschaften?
Als Referenz habe ich Jeffrey Richters Meinungen hinzugefügt:
• Eine Eigenschaft kann schreibgeschützt oder schreibgeschützt sein. Der Feldzugriff ist immer lesbar und beschreibbar. Wenn Sie eine Eigenschaft definieren, ist es am besten, Accessor-Methoden zum Abrufen und Festlegen anzubieten.
• Eine Eigenschaftsmethode kann eine Ausnahme auslösen. Der Feldzugriff löst niemals eine Ausnahme aus.
• Eine Eigenschaft kann nicht als out- oder ref-Parameter an eine Methode übergeben werden. ein Feld kann. Der folgende Code wird beispielsweise nicht kompiliert:
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 Durchführung einer Thread-Synchronisierung, die den Thread für immer stoppen kann. Daher sollte eine Eigenschaft nicht verwendet werden, wenn eine Thread-Synchronisierung erforderlich ist. In dieser Situation wird ein Verfahren bevorzugt. Wenn auf Ihre Klasse remote zugegriffen werden kann (z. B. wird Ihre Klasse von System.MashalByRefObject abgeleitet), ist der Aufruf der Eigenschaftsmethode sehr langsam. Daher wird eine Methode einer Eigenschaft vorgezogen. 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. Ein Feld gibt jedes Mal den gleichen Wert zurück. Die System.DateTime-Klasse verfügt über 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 sich, dass die Klasse behoben werden kann, indem Now anstelle einer Eigenschaft zu einer Methode gemacht wird.
• Eine Eigenschaftsmethode kann beobachtbare Nebenwirkungen verursachen. Feldzugriff nie. Mit anderen Worten, ein Benutzer eines Typs sollte in der Lage sein, verschiedene von einem Typ definierte Eigenschaften in beliebiger Reihenfolge festzulegen, ohne ein anderes Verhalten des Typs zu bemerken.
• Eine Eigenschaftsmethode erfordert möglicherweise zusätzlichen Speicher oder gibt einen Verweis auf etwas zurück, das nicht Teil des Objektstatus ist. Das Ändern des zurückgegebenen Objekts hat daher keine Auswirkungen auf das ursprüngliche Objekt. Das Abfragen eines Feldes gibt immer einen Verweis auf ein Objekt zurück, das garantiert Teil des ursprünglichen Objektstatus ist. Das Arbeiten mit einer Eigenschaft, die eine Kopie zurückgibt, kann für Entwickler sehr verwirrend sein, und diese Eigenschaft wird häufig nicht dokumentiert.
quelle
Antworten:
Jeffs Grund für die Abneigung gegen Eigenschaften ist, dass sie wie Felder aussehen. Entwickler, die den Unterschied nicht verstehen, behandeln sie also wie Felder, vorausgesetzt, sie sind billig in der Ausführung usw.
Persönlich stimme ich ihm in diesem Punkt nicht zu - ich finde, dass Eigenschaften den Client-Code viel einfacher zu lesen machen als die entsprechenden Methodenaufrufe. Ich bin damit einverstanden, dass Entwickler wissen müssen, dass Eigenschaften im Grunde genommen verschleierte Methoden sind - aber ich denke, dass es besser ist, Entwickler darüber aufzuklären, als das Lesen von Code mithilfe von Methoden zu erschweren. (Insbesondere nachdem ich gesehen habe, dass Java-Code mit mehreren Gettern und Setzern in derselben Anweisung aufgerufen wird, weiß ich, dass der entsprechende C # -Code viel einfacher zu lesen wäre. Das Gesetz von Demeter ist theoretisch alles sehr gut, aber manchmal
foo.Name.Length
wirklich das Richtige ...)(Und nein, automatisch implementierte Eigenschaften ändern daran nichts.)
Dies ist ein bisschen wie die Argumente gegen die Verwendung von Erweiterungsmethoden - ich kann die Argumentation verstehen, aber der praktische Nutzen (wenn er sparsam verwendet wird) überwiegt aus meiner Sicht den Nachteil.
quelle
Nehmen wir seine Argumente einzeln:
Dies ist ein Gewinn für Immobilien, da Sie eine genauere Kontrolle über den Zugriff haben.
Während dies meistens zutrifft, können Sie sehr gut eine Methode für ein nicht initialisiertes Objektfeld aufrufen und eine Ausnahme auslösen lassen.
Messe.
Es kann auch sehr wenig Zeit dauern.
Nicht wahr. Woher wissen Sie, dass sich der Wert des Felds nicht geändert hat (möglicherweise durch einen anderen Thread)?
Wenn es ein Fehler ist, ist es ein kleiner.
Messe.
Die meisten Proteste waren auch für Javas Getter und Setter zu sagen - und wir hatten sie eine ganze Weile ohne solche Probleme in der Praxis.
Ich denke, die meisten Probleme könnten durch eine bessere Syntaxhervorhebung (dh das Unterscheiden von Eigenschaften von Feldern) gelöst werden, damit der Programmierer weiß, was ihn erwartet.
quelle
_field
. Oder einfach nur Kleinbuchstabenfield
.Ich habe das Buch nicht gelesen und Sie haben den Teil nicht zitiert, den Sie nicht verstehen, also muss ich raten.
Einige Leute mögen keine Eigenschaften, weil sie Ihren Code dazu bringen können, überraschende Dinge zu tun.
Wenn ich tippe
Foo.Bar
, erwarten die Leser normalerweise, dass dies einfach auf ein Mitgliedsfeld der Foo-Klasse zugreift. Es ist eine billige, fast kostenlose Operation und deterministisch. Ich kann es immer und immer wieder aufrufen und jedes Mal das gleiche Ergebnis erzielen.Stattdessen kann es sich bei Eigenschaften tatsächlich um einen Funktionsaufruf handeln. Es könnte eine Endlosschleife sein. Möglicherweise wird eine Datenbankverbindung geöffnet. Bei jedem Zugriff werden möglicherweise andere Werte zurückgegeben.
Es ist ein ähnliches Argument, warum Linus C ++ hasst. Ihr Code kann für den Leser überraschend wirken. Er hasst das Überladen von Bedienern:
a + b
bedeutet nicht unbedingt eine einfache Hinzufügung. Dies kann eine äußerst komplizierte Operation bedeuten, genau wie die C # -Eigenschaften. Es kann Nebenwirkungen haben. Es kann alles tun.Ehrlich gesagt denke ich, dass dies ein schwaches Argument ist. Beide Sprachen sind voll von solchen Dingen. (Sollten wir auch in C # eine Überladung des Operators vermeiden? Schließlich kann dort dasselbe Argument verwendet werden.)
Eigenschaften ermöglichen Abstraktion. Wir können so tun , als wäre etwas ein reguläres Feld, und es so verwenden, als wäre es eines, und müssen uns keine Gedanken darüber machen, was sich hinter den Kulissen abspielt.
Das wird normalerweise als eine gute Sache angesehen, aber es hängt offensichtlich davon ab, dass der Programmierer aussagekräftige Abstraktionen schreibt. Ihre Eigenschaften sollten sich wie Felder verhalten. Sie sollten keine Nebenwirkungen haben, sie sollten keine teuren oder unsicheren Operationen durchführen. Wir wollen sie als Felder betrachten können.
Ich habe jedoch einen anderen Grund, sie nicht perfekt zu finden. Sie können nicht durch Verweis auf andere Funktionen übergeben werden.
Felder können als übergeben werden
ref
, sodass eine aufgerufene Funktion direkt darauf zugreifen kann. Funktionen können als Delegaten übergeben werden, sodass eine aufgerufene Funktion direkt darauf zugreifen kann.Eigenschaften ... können nicht.
Das ist Scheiße.
Das heißt aber nicht, dass Eigenschaften böse sind oder nicht verwendet werden sollten. Für viele Zwecke sind sie großartig.
quelle
ref
Parameter verfügbar macht. ein Mitglied (z. B.Foo
) eines Formularsvoid Foo<T>(ActionByRef<Point,T> proc, ref T param)
mit einem speziellen Attribut, in das der Compiler umgewandeltthing.Foo.X+=someVar;
wirdFoo((ref Point it, ref int param)=>it.X += param, ref someVar);
. Da das Lambda ein statischer Delegat ist, wäre kein Abschluss erforderlich, und der Benutzer eines Objekts könnte jeden Speicherort, der die Eigenschaft unterstützt, als echtenref
Parameter verwenden (und ihn alsref
Parameter an andere Methoden übergeben ).Im Jahr 2009 schien dieser Rat lediglich ein Bauchweh der Sorte Who Moved My Cheese zu sein . Heute ist es fast lächerlich veraltet.
Ein sehr wichtiger Punkt, den viele Antworten auf Zehenspitzen zu beantworten scheinen, aber nicht direkt ansprechen, ist, dass diese angeblichen "Gefahren" von Eigenschaften ein absichtlicher Bestandteil des Framework-Designs sind!
Ja, Eigenschaften können:
Geben Sie verschiedene Zugriffsmodifikatoren für den Getter und den Setter an. Dies ist ein Vorteil gegenüber Feldern. Ein übliches Muster besteht darin, einen öffentlichen Getter und einen geschützten oder internen Setter zu haben, eine sehr nützliche Vererbungstechnik, die nur von Feldern allein erreicht werden kann.
Eine Ausnahme auslösen. Bis heute ist dies eine der effektivsten Methoden zur Validierung, insbesondere bei der Arbeit mit UI-Frameworks, die Datenbindungskonzepte beinhalten. Es ist viel schwieriger sicherzustellen, dass ein Objekt bei der Arbeit mit Feldern in einem gültigen Zustand bleibt.
Die Ausführung dauert lange. Der gültige Vergleich ist hier mit Methoden , die gleich lange dauern - keine Felder . Für die Aussage "eine Methode wird bevorzugt" wird keine andere Grundlage angegeben als die persönliche Präferenz eines Autors.
Gibt bei nachfolgenden Ausführungen andere Werte von seinem Getter zurück. Dies scheint fast wie ein Witz in unmittelbarer Nähe zu dem Punkt, an dem die Tugenden von
ref
/out
parameter mit Feldern gepriesen werden, deren Wert eines Feldes nach einemref
/out
-Aufruf garantiert und unvorhersehbar von seinem vorherigen Wert abweicht.Wenn wir über den spezifischen (und praktisch akademischen) Fall eines Single-Threaded-Zugriffs ohne afferente Kopplungen sprechen, ist es ziemlich gut verstanden, dass es nur ein schlechtes Eigenschaftsdesign ist, Nebenwirkungen zu haben, die den sichtbaren Zustand ändern, und vielleicht ist es mein Gedächtnis verblassen, aber ich kann mich einfach nicht an Beispiele erinnern, bei denen Leute
DateTime.Now
jedes Mal den gleichen Wert verwenden und erwarten, dass er herauskommt. Zumindest keine Fälle, in denen sie es mit einer Hypothese nicht genauso schlimm vermasselt hättenDateTime.Now()
.Verursachen Sie beobachtbare Nebenwirkungen - und das ist natürlich genau der Grund, warum Eigenschaften überhaupt als Sprachmerkmal erfunden wurden. Laut den Microsoft- Richtlinien für das Eigenschaftsdesign sollte die Setterreihenfolge keine Rolle spielen, da dies sonst eine zeitliche Kopplung implizieren würde . Natürlich können Sie keine zeitliche Kopplung mit Feldern allein erreichen, aber das liegt nur daran, dass Sie mit Feldern allein überhaupt kein bedeutungsvolles Verhalten verursachen können, bis eine Methode ausgeführt wird.
Eigenschaftszugriffsmethoden können tatsächlich dazu beitragen , bestimmte Arten der zeitlichen Kopplung zu verhindern, indem sie das Objekt in einen gültigen Zustand versetzen, bevor eine Aktion ausgeführt wird. Wenn beispielsweise eine Klasse ein
StartDate
und ein hatEndDate
, können Sie auch dasEndDate
Vorher- SetzenStartDate
festlegen, um dasStartDate
Zurück zu erzwingen . Dies gilt auch in Umgebungen mit mehreren Threads oder asynchronen Umgebungen, einschließlich des offensichtlichen Beispiels einer ereignisgesteuerten Benutzeroberfläche.Andere Dinge, die Eigenschaften tun können, die Felder nicht enthalten können:
Type
oderName
so abgeleiteten Klasse, kann interessante, aber dennoch konstante Metadaten über sich selbst liefern.Item(i)
Anrufen arbeiten musste, als wunderbare Sache erkennen wird.Richter ist eindeutig ein produktiver Autor und weiß viel über CLR und C #, aber ich muss sagen, es scheint, als hätte er diesen Rat ursprünglich geschrieben (ich bin mir nicht sicher, ob er in seinen neueren Revisionen enthalten ist - ich hoffe aufrichtig nicht). dass er einfach alte Gewohnheiten nicht loslassen wollte und Probleme hatte, die Konventionen von C # zu akzeptieren (im Vergleich zu C ++ zum Beispiel).
Damit meine ich, dass sein Argument "Eigenschaften als schädlich angesehen" im Wesentlichen auf eine einzige Aussage hinausläuft : Eigenschaften sehen aus wie Felder, aber sie verhalten sich möglicherweise nicht wie Felder. Und das Problem mit der Aussage ist, dass sie nicht wahr oder bestenfalls sehr irreführend ist. Eigenschaften nicht aussehen wie Felder - zumindest werden sie nicht angenommen , wie Felder zu suchen.
Es gibt zwei sehr starke Codierungskonventionen in C # mit ähnlichen Konventionen, die von anderen CLR-Sprachen geteilt werden, und FXCop wird Sie anschreien, wenn Sie diese nicht befolgen:
Somit besteht keine Unklarheit darüber, ob
Foo.Bar = 42
es sich um einen Eigenschafts- oder einen Feldzugriff handelt. Es ist ein Eigenschafts-Accessor und sollte wie jede andere Methode behandelt werden - es kann langsam sein, es kann eine Ausnahme auslösen usw. Das liegt in der Natur der Abstraktion - es liegt ganz im Ermessen der deklarierenden Klasse, wie sie reagieren soll. Klassendesigner sollten das Prinzip der geringsten Überraschung anwenden, aber Anrufer sollten nichts von einer Immobilie annehmen, außer dass sie das tut, was sie verspricht. Das ist mit Absicht.Die Alternative zu Eigenschaften sind Getter / Setter-Methoden überall. Das ist der Java-Ansatz, der von Anfang an umstritten war . Es ist in Ordnung, wenn das deine Tasche ist, aber es ist einfach nicht so, wie wir im .NET-Camp rollen. Wir versuchen, zumindest innerhalb der Grenzen eines statisch typisierten Systems, das zu vermeiden, was Fowler Syntaktisches Rauschen nennt . Wir wollen keine zusätzlichen Klammern, zusätzlichen
get
/set
Warzen oder zusätzlichen Methodensignaturen - nicht, wenn wir sie ohne Verlust der Klarheit vermeiden können.Sagen Sie, was Sie wollen, aber es
foo.Bar.Baz = quux.Answers[42]
wird immer viel einfacher zu lesen sein alsfoo.getBar().setBaz(quux.getAnswers().getItem(42))
. Und wenn Sie täglich Tausende von Zeilen davon lesen, macht das einen Unterschied.(Und wenn Ihre natürliche Antwort auf den obigen Absatz lautet: "Sicher ist es schwer zu lesen, aber es wäre einfacher, wenn Sie ihn in mehrere Zeilen aufteilen", dann muss ich leider sagen, dass Sie den Punkt völlig verfehlt haben .)
quelle
Ich sehe keine Gründe, warum Sie Eigenschaften im Allgemeinen nicht verwenden sollten.
Automatische Eigenschaften in C # 3+ vereinfachen die Syntax nur ein wenig (a la syntatischer Zucker).
quelle
Es ist nur die Meinung einer Person. Ich habe einige C # -Bücher gelesen und ich habe noch niemanden gesehen, der sagt "benutze keine Eigenschaften".
Ich persönlich denke, dass Eigenschaften eines der besten Dinge an c # sind. Mit ihnen können Sie den Status über einen beliebigen Mechanismus anzeigen. Sie können das erste Mal, wenn etwas verwendet wird, träge instanziieren und das Festlegen eines Werts usw. überprüfen. Wenn Sie sie verwenden und schreiben, stelle ich mir Eigenschaften nur als Setter und Getter vor, die eine viel schönere Syntax haben.
Was die Vorbehalte mit Eigenschaften betrifft, gibt es ein paar. Einer ist wahrscheinlich ein Missbrauch von Eigenschaften, der andere kann subtil sein.
Erstens sind Eigenschaften Arten von Methoden. Es kann überraschend sein, wenn Sie eine komplizierte Logik in eine Eigenschaft einfügen, da die meisten Benutzer einer Klasse erwarten, dass die Eigenschaft ziemlich leicht ist.
Z.B
Ich finde, dass die Verwendung von Methoden für diese Fälle hilft, eine Unterscheidung zu treffen.
Zweitens, und was noch wichtiger ist, können Eigenschaften Nebenwirkungen haben, wenn Sie sie beim Debuggen bewerten.
Angenommen, Sie haben eine Eigenschaft wie diese:
Angenommen, Sie haben eine Ausnahme, die auftritt, wenn zu viele Abfragen durchgeführt werden. Ratet mal, was passiert, wenn Sie mit dem Debuggen beginnen und die Eigenschaft im Debugger verschieben? Schlechte Dinge. Vermeiden Sie dies! Durch Betrachten der Eigenschaft wird der Status des Programms geändert.
Dies sind die einzigen Einschränkungen, die ich habe. Ich denke, die Vorteile von Immobilien überwiegen bei weitem die Probleme.
quelle
Dieser Grund muss in einem ganz bestimmten Kontext angegeben worden sein. Es ist normalerweise umgekehrt - es wird empfohlen, Eigenschaften zu verwenden, da diese Ihnen eine Abstraktionsebene bieten, mit der Sie das Verhalten einer Klasse ändern können, ohne die Clients zu beeinflussen ...
quelle
Ich kann nicht anders, als die Details von Jeffrey Richters Meinungen herauszusuchen:
Falsch: Felder können als schreibgeschützt markiert werden, sodass nur der Konstruktor des Objekts darauf schreiben kann.
Falsch: Durch die Implementierung einer Klasse kann der Zugriffsmodifikator eines Felds von öffentlich in privat geändert werden. Versuche, private Felder zur Laufzeit zu lesen, führen immer zu einer Ausnahme.
quelle
Ich stimme Jeffrey Richter nicht zu, aber ich kann mir vorstellen, warum er keine Immobilien mag (ich habe sein Buch nicht gelesen).
Obwohl Eigenschaften wie Benutzer (in Bezug auf die Implementierung) sind, erwarte ich als Benutzer einer Klasse, dass sich ihre Eigenschaften "mehr oder weniger" wie ein öffentliches Feld verhalten, z.
Leider habe ich Eigenschaften gesehen, die sich nicht so verhalten haben. Das Problem sind jedoch nicht die Eigenschaften selbst, sondern die Personen, die sie implementiert haben. Es erfordert also nur etwas Bildung.
quelle
Es gibt eine Zeit, in der ich erwäge, keine Eigenschaften zu verwenden, und zwar schriftlich .Net Compact Framework-Code. Der CF-JIT-Compiler führt nicht die gleiche Optimierung wie der Desktop-JIT-Compiler durch und optimiert keine einfachen Eigenschaftszugriffsberechtigten. In diesem Fall führt das Hinzufügen einer einfachen Eigenschaft dazu, dass bei Verwendung eines öffentlichen Felds eine geringe Menge an Code aufgebläht wird. Normalerweise ist dies kein Problem, aber in der Compact Framework-Welt sind Sie fast immer mit engen Speicherbeschränkungen konfrontiert, sodass selbst winzige Einsparungen wie diese zählen.
quelle
Sie sollten es nicht vermeiden, sie zu verwenden, aber Sie sollten sie aus den von anderen Mitwirkenden angegebenen Gründen mit Qualifikation und Sorgfalt verwenden.
Ich habe einmal eine Eigenschaft namens "Kunden" gesehen, die intern einen Out-of-Process-Aufruf einer Datenbank geöffnet und die Kundenliste gelesen hat. Der Client-Code hatte ein 'für (int i to Customers.Count)', was bei jeder Iteration und für den Zugriff des ausgewählten Kunden einen separaten Aufruf der Datenbank verursachte. Dies ist ein ungeheuerliches Beispiel, das das Prinzip demonstriert, die Immobilie sehr leicht zu halten - selten mehr als ein interner Feldzugang.
Ein Argument für die Verwendung von Eigenschaften besteht darin, dass Sie damit den festgelegten Wert überprüfen können. Ein weiterer Grund ist, dass der Wert der Eigenschaft ein abgeleiteter Wert sein kann und kein einzelnes Feld, wie TotalValue = Betrag * Menge.
quelle
Persönlich verwende ich Eigenschaften nur beim Erstellen einfacher Get / Set-Methoden. Ich weiche davon ab, wenn ich zu komplizierten Datenstrukturen komme.
quelle
Das Argument geht davon aus, dass Eigenschaften schlecht sind, weil sie wie Felder aussehen, aber überraschende Aktionen ausführen können. Diese Annahme wird durch die Erwartungen der .NET-Programmierer ungültig:
Eigenschaften sehen nicht wie Felder aus. Felder sehen aus wie Eigenschaften.
Ein Feld ist also wie eine Eigenschaft, die garantiert niemals eine Ausnahme auslöst.
Ein Feld ist also wie eine Eigenschaft, verfügt jedoch über zusätzliche Funktionen: Übergabe an eine
ref
/out
akzeptierende Methode.Ein Feld ist also wie eine schnelle Eigenschaft.
Ein Feld ist also wie eine Eigenschaft, die garantiert denselben Wert zurückgibt, es sei denn, das Feld wurde auf einen anderen Wert festgelegt.
Auch hier ist ein Feld eine Eigenschaft, die dies garantiert nicht tut.
Dieser mag überraschend sein, aber nicht, weil es eine Eigenschaft ist, die dies tut. Es ist vielmehr so, dass kaum jemand veränderbare Kopien in Eigenschaften zurückgibt, so dass ein Fall von 0,1% überraschend ist.
quelle
Das Aufrufen von Methoden anstelle von Eigenschaften verringert die Lesbarkeit des aufrufenden Codes erheblich. In J # war die Verwendung von ADO.NET beispielsweise ein Albtraum, da Java keine Eigenschaften und Indexer unterstützt (die im Wesentlichen Eigenschaften mit Argumenten sind). Der resultierende Code war extrem hässlich, mit Methodenaufrufen in leeren Klammern überall.
Die Unterstützung von Eigenschaften und Indexern ist einer der grundlegenden Vorteile von C # gegenüber Java.
quelle