2010-10-12 11 views
54

Ich muss eine lesen Sie nur Eigenschaft auf meinem Typ implementieren. Darüber hinaus wird der Wert dieser Eigenschaft im Konstruktor festgelegt und nicht geändert (ich schreibe eine Klasse, die benutzerdefinierte UI-Routinen für WPF verfügbar macht, aber das spielt keine Rolle).So implementieren Sie eine schreibgeschützte Eigenschaft

ich zwei Möglichkeiten sehen, es zu tun:

  1. class MyClass 
    { 
        public readonly object MyProperty = new object(); 
    } 
    
  2. class MyClass 
    { 
        private readonly object my_property = new object(); 
        public object MyProperty { get { return my_property; } } 
    } 
    

Mit all diesen FxCop Fehler zu sagen, dass ich nicht öffentlichen Membervariablen haben sollte, scheint es, dass die zweite ist der richtige Weg, es zu tun. Richtig?

Gibt es einen Unterschied zwischen einer Get-Only-Eigenschaft und einem Read-Only-Member in diesem Fall?

Ich würde mich über jeden Kommentar/Beratung/etc. Freuen.

+20

Ich wünsche manchmal, dass die Auto-Eigenschaft-Syntax enthalten ein "Get; Nur lesen; Option. –

+0

Mögliches Duplikat von [C#, Unveränderlichkeit und öffentliche readonly Felder] (http://stackoverflow.com/questions/2249980/c-immutability-and-public-readonly-fields) – user

Antwort

31

Versionierung:
Ich denke, es ist nicht viel Unterschied macht, wenn Sie daran interessiert sind nur in Quelle Kompatibilität sind.
Die Verwendung einer Eigenschaft ist besser für die Binärkompatibilität, da Sie sie je nach Bibliothek durch eine Eigenschaft ersetzen können, die über einen Setter verfügt, ohne den kompilierten Code zu unterbrechen.

Convention:
Sie sind nach der Konvention. In Fällen wie diesen, in denen die Unterschiede zwischen den beiden Möglichkeiten relativ gering sind, ist die Einhaltung der Konvention besser. Ein Fall, in dem es zurückkommen könnte, um dich zu beißen, ist ein Code, der auf der Reflexion basiert.Es akzeptiert möglicherweise nur Eigenschaften und keine Felder, z. B. einen Eigenschaftseditor/-viewer.

Serialisierung
Wechsel von Feld Eigenschaft wird wahrscheinlich eine Menge Serializer brechen. Und AFAIK XmlSerializer serialisiert nur öffentliche Eigenschaften und nicht öffentliche Felder.

Mit einem Autoproperty
eine weitere gemeinsame Variation eine autoproperty mit einem privaten Setter verwendet. Während dies kurz ist und eine Eigenschaft, erzwingt es nicht die Lesbarkeit. Also bevorzuge ich die anderen.

Read-Only-Feld wird selfdocumenting
Es ist ein Vorteil des Feldes aber:
Es macht deutlich, auf einen Blick auf die öffentliche Schnittstelle, die es tatsächlich unveränderlich ist (abgesehen von Reflexion). Während im Falle einer Eigenschaft können Sie nur sehen, dass Sie kann es nicht ändern, so dass Sie auf die Dokumentation oder Implementierung beziehen müssen.

Aber um ehrlich zu sein, benutze ich die erste ziemlich oft im Anwendungscode, da ich faul bin. In Bibliotheken bin ich normalerweise gründlicher und folge der Konvention.

C# 6.0 fügt Nur-Lese-Auto Eigenschaften

public object MyProperty { get; } 

Also, wenn Sie nicht brauchen, ältere Compiler unterstützen Sie eine wirklich schreibgeschützte Eigenschaft mit dem Code haben, der als Nur-Lese-Feld nur so kurz ist.

49

Die zweite Möglichkeit ist die bevorzugte Option.

private readonly int MyVal = 5; 

public int MyProp { get { return MyVal;} } 

Dadurch wird sichergestellt, dass MyVal nur bei der Initialisierung (auch in einem Konstruktor festgelegt werden kann) zugeordnet werden kann.

Wie Sie festgestellt haben - auf diese Weise geben Sie kein internes Mitglied frei, so dass Sie die interne Implementierung in Zukunft ändern können.

+0

thanks. Die erste Option gewährleistet das gleiche. Was denkst du ist der Grund, warum dieser die bevorzugte Option ist? – akonsu

+0

Gibt es einen Grund, warum wir 'public int MyProp {get; privates Set; } '? Ich weiß, dass es nicht wirklich readonly ist, aber es ist verdammt nah dran. –

+3

@Mike Hofer - Da der Int als _readonly_ deklariert ist, können Sie ihn nicht außerhalb eines Konstruktors ändern. – Oded

4

Die zweite Methode ist wegen der Verkapselung bevorzugt. Sie können das readonly-Feld natürlich öffentlich haben, aber das widerspricht C# -Idiomen, in denen Sie Datenzugriff über Eigenschaften und nicht über Felder haben.

Der Grund dafür ist, dass die Eigenschaft eine öffentliche Schnittstelle definiert und wenn die Backing-Implementierung für diese Eigenschaft ändert, Sie nicht den Rest des Codes zu brechen, weil die Implementierung hinter einer Schnittstelle versteckt ist.

5

Ich stimme zu, dass der zweite Weg vorzuziehen ist. Der einzige wirkliche Grund für diese Präferenz ist die allgemeine Präferenz, dass .NET-Klassen keine öffentlichen Felder haben. Wenn dieses Feld jedoch nur gelesen wird, kann ich nicht erkennen, dass es echte Einwände geben würde, abgesehen von mangelnder Übereinstimmung mit anderen Eigenschaften. Der wirkliche Unterschied zwischen einem Readonly-Feld und einer Get-Only-Eigenschaft besteht darin, dass das Readonly-Feld eine Garantie dafür bietet, dass sein Wert sich während der Lebensdauer des Objekts nicht ändert und eine Get-Only-Eigenschaft dies nicht tut.

+0

das ist eine gute Erklärung. Vielen Dank. – akonsu

8

Sie können dies tun:

public int Property { get { ... } private set { ... } } 
+4

Ja, Sie können, aber mit dieser Technik garantieren Sie nur, dass die Eigenschaft nicht von den Verbrauchern der Klasse geändert werden kann, nicht, dass es für die Lebensdauer des Objekts konstant bleibt. –

35

Mit der Einführung von C# 6 (in VS 2015), kann man nun get -nur automatische Eigenschaften aufweist, in dem das implizite Trägerfeld readonly (dh Werte im Konstruktor zugeordnet werden können, aber nicht an anderer Stelle):

public string Name { get; } 

public Customer(string name) // Constructor 
{ 
    Name = name; 
} 

private void SomeFunction() 
{ 
    Name = "Something Else"; // Compile-time error 
} 

und Sie können jetzt auch Eigenschaften initialisieren (mit oder ohne Setter) inline:

public string Name { get; } = "Boris"; 

zurück auf die Frage Bezug genommen, das Ihnen die Vorteile der Option gibt 2 (öffentliches Mitglied ist eine Eigenschaft, nicht ein Feld) mit der Kürze der Option 1.

Leider bietet es keine Garantie der Unveränderlichkeit auf der Ebene der öffentlichen Schnittstelle (wie in @ CodesInChaos Punkt zur Selbstdokumentation), weil zu einem Verbraucher der Klasse, ohne Setter ist nicht zu unterscheiden von einem privaten Setter.

+0

Gute Informationen über neue Syntax, jedoch können Sie einen privaten Setter reflektieren und aktivieren und/oder einen Wert in ein Hintergrundfeld laden (unabhängig von Zugriffsmodifikatoren). Es ist auch möglich, zwischen einem privaten Setter und dem Fehlen eines Setter zu unterscheiden Laufzeit mit Reflektion. –

+1

@Shaun: Guter Punkt - es gibt * viele * Dinge, die du mit Reflektion tun kannst! Eine Reihe von Möglichkeiten widerspricht wahrscheinlich der Absicht des ursprünglichen Programmierers oder sogar der Sprachdesigner, aber sagst du, dass "readonly" -Felder durch Reflektion verändert werden können (ich kenne die Antwort nicht, aber es scheint problematisch)? –

+2

Ich sagte nicht, dass insbesondere, nein, aber die Antwort ist: Ja .. Sie können. https://gist.github.com/wilson0x4d/a053c0fd57892d357b2c Wenn Sie denken, dass das problematisch ist, warten Sie einfach, bis Sie lernen, dass jedes Betriebssystem auf dem Planeten einen Mechanismus in Position hat, in dem ein Prozess den Speicher irgendeines anderen Prozesses lesen/schreiben kann (gegeben ausreichende Ausführungsrechte, das heißt.) Deshalb kann kein softwarebasiertes System jemals wirklich sicher sein, aber ich schweife ab, das hat mit der ursprünglichen Frage nicht viel zu tun :) auch wenn es interessant ist! –