Ist die Gewährleistung der Unveränderlichkeit eine Rechtfertigung für die Freilegung eines Feldes anstelle einer Immobilie?

10

Die allgemeine Anleitung für C # besteht darin, eine Eigenschaft immer über einem öffentlichen Feld zu verwenden. Dies ist sinnvoll. Wenn Sie ein Feld verfügbar machen, werden viele Implementierungsdetails verfügbar gemacht. Mit einer Eigenschaft kapseln Sie dieses Detail so, dass es keinen Code verbraucht und Implementierungsänderungen von Schnittstellenänderungen entkoppelt werden.

Ich frage mich jedoch, ob es beim Umgang mit dem readonlySchlüsselwort manchmal eine gültige Ausnahme von dieser Regel gibt . Indem Sie dieses Schlüsselwort auf ein öffentliches Feld anwenden, geben Sie eine zusätzliche Garantie: Unveränderlichkeit. Dies ist nicht nur ein Implementierungsdetail, Unveränderlichkeit ist etwas, an dem ein Verbraucher interessiert sein könnte. Die Verwendung eines readonlyFelds macht es Teil des öffentlichen Auftrags und etwas, das nicht durch zukünftige Änderungen oder Vererbung gebrochen werden kann, ohne auch die öffentliche Schnittstelle ändern zu müssen. Das kann eine Immobilie nicht bieten.

Ist die Gewährleistung der Unveränderlichkeit readonlyin einigen Fällen ein legitimer Grund, ein Feld einer Immobilie vorzuziehen?

(Zur Verdeutlichung sage ich sicherlich nicht, dass Sie diese Wahl immer treffen sollten , nur weil das Feld im Moment unveränderlich ist, nur wenn es als Teil des Klassendesigns sinnvoll ist und beabsichtigt ist, Unveränderlichkeit in den Vertrag aufzunehmen. Ich interessiere mich hauptsächlich für Antworten, die sich darauf konzentrieren, ob dies gerechtfertigt ist, und nicht für bestimmte Fälle, in denen dies nicht der Fall ist, z. B. wenn das Mitglied auf einem Mitglied sein interfacemuss oder wenn Sie faul laden möchten.)

Ben Aaronson
quelle
1
@gnat Zur Verdeutlichung verwenden sie mit "ReadOnly-Eigenschaft" die VB.NET-Terminologie, dh eine Eigenschaft ohne Setter, kein schreibgeschütztes Feld. Für die Zwecke meiner Frage sind die beiden Optionen, die diese Frage vergleicht, gleichwertig - beide verwenden Eigenschaften.
Ben Aaronson

Antworten:

6

Öffentliche statische schreibgeschützte Felder sind sicherlich in Ordnung. Sie werden für bestimmte Situationen empfohlen, z. B. wenn Sie ein benanntes, konstantes Objekt möchten, das constSchlüsselwort jedoch nicht verwenden können .

Schreibgeschützte Mitgliederfelder sind etwas kniffliger. Es gibt nicht viel Vorteil gegenüber einer Immobilie ohne einen öffentlichen Setter, und es gibt einen großen Nachteil: Felder können nicht Mitglieder von Schnittstellen sein. Wenn Sie sich also dazu entschließen, Ihren Code so umzugestalten, dass er für eine Schnittstelle anstelle eines konkreten Typs verwendet wird, müssen Sie Ihre schreibgeschützten Felder in Eigenschaften mit öffentlichen Gettern ändern.

Eine öffentliche nicht virtuelle Eigenschaft ohne geschützten Setter bietet Schutz vor Vererbung, es sei denn, die geerbten Klassen haben Zugriff auf das Sicherungsfeld. Sie können diesen Vertrag in der Basisklasse vollständig durchsetzen.

In diesem Fall ist es kein großes Hindernis, die "Unveränderlichkeit" des Mitglieds nicht ändern zu können, ohne die öffentliche Schnittstelle der Klasse zu ändern. Wenn Sie ein öffentliches Feld in eine Eigenschaft ändern möchten, funktioniert dies normalerweise reibungslos, mit der Ausnahme, dass Sie sich mit zwei Fällen befassen müssen:

  1. Alles, was in ein Mitglied dieses Objekts schreibt, wie: object.Location.X = 4ist nur möglich, wenn Locationes sich um ein Feld handelt. Dies ist jedoch für ein schreibgeschütztes Feld nicht relevant, da Sie eine schreibgeschützte Struktur nicht ändern können. (Dies setzt voraus, dass Locationes sich um einen Werttyp handelt. Wenn nicht, würde dieses Problem ohnehin nicht auftreten, da readonlyes nicht vor solchen Dingen schützt.)
  2. Jeder Aufruf einer Methode, die den Wert als outoder übergibt ref. Auch dies ist nicht wirklich relevant für einen Nur - Lese - Bereich, da outund refParameter zu erhalten geändert neigen, und es ist ein Compiler - Fehler ohnehin einen Nur - Lese - Wert oder ref passieren.

Mit anderen Worten, obwohl das Konvertieren eines schreibgeschützten Felds in eine schreibgeschützte Eigenschaft die Binärkompatibilität beeinträchtigt, müsste anderer Quellcode nur ohne Änderungen neu kompiliert werden, um diese Änderung zu verarbeiten. Das macht die Garantie wirklich leicht zu brechen.

Ich sehe keine Vorteile für das readonlyMitgliederfeld, und die Unfähigkeit, so etwas auf einer Schnittstelle zu haben, ist für mich ein Nachteil genug, dass ich es nicht verwenden würde.

Erik
quelle
Warum sind öffentliche statische schreibgeschützte Felder aus Interesse in Ordnung? Liegt es nur daran, dass durch das Ausschließen von Instanzmethoden die meisten Anwendungsfälle für Eigenschaften in unveränderlichen Feldern (wie Trefferzählung, verzögertes Laden usw.) entfernt werden?
Ben Aaronson
Der Hauptnachteil eines öffentlichen schreibgeschützten Felds gegenüber einer schreibgeschützten Eigenschaft ist weg - statische Elemente können nicht Teil von Schnittstellen sein, sodass sie auf einer ähnlichen Grundlage stehen. Eine schreibgeschützte statische Eigenschaft benötigt entweder Speicher, der initialisiert wird, oder sie muss ihren Rückgabewert bei jedem Aufruf neu erstellen, während ein schreibgeschütztes Feld eine einfachere Syntax hat und vom statischen Konstruktor initialisiert wird. Daher denke ich, dass ein öffentliches statisches schreibgeschütztes Feld das Potenzial für eine stärkere Optimierung durch die JIT hat, ohne die Nachteile, die Sie normalerweise durch das Offenlegen eines Feldes hätten.
Erik
2

Nach meiner Erfahrung ist das readonlySchlüsselwort nicht die Magie, die es verspricht.

Sie haben beispielsweise eine Klasse, in der der Konstruktor früher einfach war. Angesichts der Verwendung (und der Tatsache, dass einige der Klasseneigenschaften / -felder nach der Erstellung unveränderlich sind) könnten Sie denken, dass dies keine Rolle spielt, also haben Sie das readonlySchlüsselwort verwendet.

Später wird die Klasse komplexer, ebenso wie ihr Konstruktor. (Nehmen wir an, dies geschieht in einem Projekt, das entweder experimentell ist oder eine ausreichend hohe Geschwindigkeit hat, die außerhalb des Bereichs / der Kontrolle zoomt, aber Sie müssen dafür sorgen, dass es irgendwie funktioniert.) Sie werden feststellen, dass Felder, die readonlynur innerhalb des Konstruktors geändert werden können - Sie kann es in Methoden nicht ändern, selbst wenn diese Methode nur von einem Konstruktor aufgerufen wird.

Diese technische Einschränkung wurde von den Designern von C # anerkannt und wird möglicherweise in einer zukünftigen Version behoben. Aber bis es behoben ist, ist die Verwendung von readonlyrestriktiver und kann zu einem schlechten Codierungsstil führen (alles in die Konstruktormethode einfügen).

Zur Erinnerung: Weder eine readonlynoch nicht öffentlich festgelegte Eigenschaft bietet eine Garantie für ein "vollständig initialisiertes Objekt". Mit anderen Worten, es ist der Designer einer Klasse, der entscheidet, was "vollständig initialisiert" bedeutet und wie er vor versehentlichem Gebrauch geschützt werden soll. Ein solcher Schutz ist im Allgemeinen nicht narrensicher, was bedeutet, dass jemand einen Weg finden könnte, ihn zu umgehen.

rwong
quelle
1
Ich ziehe es vor, Konstruktoren einfach zu halten - siehe brendan.enrick.com/post/… , blog.ploeh.dk/2011/03/03/InjectionConstructorsshouldbesimple , also ermutigt schreibgeschützt nur einen guten Codierungsstil
AlexFoxGill
1
Ein Konstruktor kann ein readonlyFeld als outoder refParameter an andere Methoden übergeben, die es dann nach Belieben ändern können.
Supercat
1

Felder sind IMMER Implementierungsdetails. Sie möchten Ihre Implementierungsdetails nicht offenlegen.

Ein grundlegender Grundsatz des Software-Engineerings ist, dass wir nicht wissen, welche Anforderungen in Zukunft gestellt werden. Auch readonlywenn Ihr Fachgebiet heute gut ist, gibt es keine Anhaltspunkte dafür, dass es Teil der Anforderungen von morgen sein wird. Was ist, wenn morgen ein Trefferzähler implementiert werden muss, um zu bestimmen, wie oft auf dieses Feld zugegriffen wird? Was ist, wenn Sie zulassen müssen, dass Unterklassen das Feld überschreiben?

Eigenschaften sind so einfach zu verwenden und so viel flexibler als öffentliche Felder, dass ich mir keinen einzigen Anwendungsfall vorstellen kann, in dem ich sowohl c # als meine Sprache auswählen als auch öffentliche schreibgeschützte Felder für eine Klasse verwenden würde. Wenn die Leistung so gering wäre, dass ich den zusätzlichen Methodenaufruf nicht ertragen könnte, würde ich wahrscheinlich eine andere Sprache verwenden.

Nur weil wir können etwas mit der Sprache tun, bedeutet nicht , wir sollten etwas mit der Sprache tun.

Ich frage mich tatsächlich, ob die Sprachdesigner es bereuen, Felder veröffentlicht zu haben. Ich kann mich nicht erinnern, wann ich das letzte Mal in Betracht gezogen habe, nicht konstante öffentliche Felder in meinem Code zu verwenden.

Stephen
quelle
1
Ich verstehe, wie wichtig es ist, in Zukunft Flexibilität aufzubauen, aber wenn ich eine Klasse schreibe, wie zum Beispiel Rationalmit öffentlichen, unveränderlichen Numeratorund DenominatorMitgliedern, kann ich sehr sicher sein, dass diese Mitglieder kein Lazy-Loading oder einen Trefferzähler benötigen oder ähnlich?
Ben Aaronson
Aber können Sie sicher sein , dass die Umsetzung unter Verwendung von floatTypen für die Numeratorund Denominatornicht müssen geändert werden doubles?
Stephen
1
Nun, ähm, nein, sie sollten definitiv weder floatnoch sein double. Sie sollten sein int, longoder BigInteger. Vielleicht müssen sich diese ändern ... aber wenn ja, müssten sie sich auch in der öffentlichen Oberfläche ändern. Wenn Sie also wirklich Eigenschaften verwenden, wird dieses Detail nicht gekapselt.
Ben Aaronson
-3

Nein, das ist keine Rechtfertigung.

Das Readonly als Garantie für Unveränderlichkeit und nicht als Rechtfertigung zu verwenden, ist eher ein Hack.

Readonly bedeutet, dass der Wert von einem Konstruktor o zugewiesen wird, wenn die Variable definiert wird.

Ich denke, es wird eine schlechte Praxis sein

Wenn Sie wirklich Unveränderlichkeit garantieren möchten, verwenden Sie eine Schnittstelle, die mehr Vor- als Nachteile hat.

Einfaches Schnittstellenbeispiel: Geben Sie hier die Bildbeschreibung ein

Pablo Granados
quelle
1
"benutze eine Schnittstelle" Wie?
Ben Aaronson