2010-02-12 20 views
34

Ich habe an vielen Stellen gelesen, dass das Freilegen von Feldern ist keine gute Idee, denn wenn Sie später zu Eigenschaften ändern möchten, müssen Sie den gesamten Code neu kompilieren, der Ihre Klasse verwendet.C#, Unveränderlichkeit und öffentliche readonly Felder

Im Falle von unveränderlichen Klassen, sehe ich jedoch nicht, warum Sie jemals zu Eigenschaften wechseln müssten - Sie werden dem Set schließlich keine Logik hinzufügen.

Irgendwelche Gedanken dazu, vermisse ich etwas?

Beispiel für den Unterschied, für diejenigen, den Code leichter als Text lesen :)

//Immutable Tuple using public readonly fields 
public class Tuple<T1,T2> 
{ 
    public readonly T1 Item1; 
    public readonly T2 Item2; 
    public Tuple(T1 item1, T2 item2) 
    { 
     Item1 = item1; 
     Item2 = item2; 
    } 
} 

//Immutable Tuple using public properties and private readonly fields 
public class Tuple<T1,T2> 
{ 
    private readonly T1 _Item1; 
    private readonly T2 _Item2; 
    public Tuple(T1 item1, T2 item2) 
    { 
     _Item1 = item1; 
     _Item2 = item2; 
    } 
    public T1 Item1 { get { return _Item1; } } 
    public T2 Item2 { get { return _Item2; } } 
} 

Natürlich könnten Sie auto-Eigenschaften verwenden (public T1 Item1 { get; private set; }), aber dies nur bekommt man ‚stimmt Unveränderlichkeit‘ als im Gegensatz zu "garantierte Unveränderlichkeit" ...

Antwort

11

Es ist offensichtlich, dass die Auslassung von Eigenschaften, die Sie nicht so etwas wie schreiben:

public T2 Item2 { get; readonly set; } 

Ich bin nicht einmal sicher, readonly ist das beste Wort „nur im Konstruktor gesetzt werden kann“ bedeuten zu verwenden, aber damit sind wir festgefahren.

Dies ist eigentlich eine Funktion, die viele Leute angefordert haben, also hoffen wir, dass es in einiger Zeit in einer hypothetischen neuen Version von C# eingeführt wird.

Siehe this related question.

+2

Hm, eine "hypothetische neue Version von C#" Ich denke, ich weiß, wer den Blog liest, den du gelesen hast;) – Benjol

+1

Ja, wir denken darüber nach. Hypothetisch. Es ist eigentlich ein schwierigeres Problem als es auf den ersten Blick scheint. –

+2

@Eric Lippert, jetzt necken Sie uns nur. Ich erwarte irgendwann einen Beitrag über die Schwierigkeiten :) – Benjol

0

Als eine Standardpraxis folge ich Ihrem zweiten Beispiel nur mit "readonly", wenn das Objekt öffentlich ist oder anfällig für unbeabsichtigte Manipulation. Ich verwende das Modell der "vereinbarten Unveränderlichkeit" in einem aktuellen Projekt, das ein Plugin-Framework erstellt. Offensichtlich wird der Schutz readonly mit vereinbarter Unveränderlichkeit entfernt.

Nur in seltenen Fällen exponiere ich ein Feld - öffentlich, intern oder anderweitig. Es fühlt sich einfach nicht richtig an, es sei denn, dass das Schreiben einer Eigenschaft {get;} mehr Zeit in Anspruch nimmt, als ich zu geben bereit bin.

0

Die Idee hinter Eigenschaften ist, dass, auch wenn Sie nicht beabsichtigen, sie jetzt oder später zu ändern, müssen Sie möglicherweise in einer unvorhergesehenen Weise. Nehmen wir an, Sie müssen den Getter ändern, um eine Art von Berechnung oder Protokollierung durchzuführen. Vielleicht müssen Sie die Ausnahmebehandlung hinzufügen. Viele mögliche Gründe.

Auch Semantik berücksichtigen. Wenn T1 ein Werttyp und kein Referenztyp ist, gibt der Zugriff auf obj.Item1 eine Kopie von _Item1 im Getter zurück, während der Zugriff auf Item1 ohne einen Getter keine Kopie abrufen würde. Das bedeutet, dass Item1 zwar intern unveränderlich sein kann, das zurückgegebene Werttypobjekt jedoch nicht. Ich kann mir keinen Grund vorstellen, warum das eine gute Sache wäre, aber es ist ein Unterschied.

+0

Was ist das mit dem Kopieren, wenn Sie über eine Eigenschaft zurückkehren? Hast du eine Referenz dafür? – Benjol

+0

Werttypen müssen kopiert werden, wenn Sie sie übergeben. So funktioniert C#. –

+1

Ja, aber warum sollte das Feld nicht auch kopiert werden? Ich kann nicht sehen, wo das ein Problem wäre. – Benjol

3

Sie müssen möglicherweise in Zukunft keine Logik zu einem Setter hinzufügen, aber Sie müssen möglicherweise eine Logik zu einem Getter hinzufügen.

Das ist ein Grund genug für mich, Eigenschaften zu verwenden, anstatt Felder freizulegen.

Wenn ich mich streng fühle, würde ich für vollständige Unveränderlichkeit gehen (explizite readonly Hintergrundfelder mit exponierten Getter und keine Setter).Wenn ich mich faul fühle, würde ich wahrscheinlich "vereinbarte Unveränderlichkeit" wählen (automatische Eigenschaften mit exponierten Gettern und privaten Settern).

11

C# 6.0 unterstützt jetzt Auto-Property-Initialisierer.

Der Auto-Property-Initialisierer erlaubt die direkte Zuweisung von Eigenschaften innerhalb ihrer Deklaration. Bei schreibgeschützten Eigenschaften kümmert sich das System um alle Zeremonien, die erforderlich sind, um sicherzustellen, dass die Eigenschaft unveränderlich ist.

Sie können initialisieren schreibgeschützte Eigenschaften in Konstruktor oder mit Auto-initializer

public class Customer 
{ 
    public Customer3(string firstName, string lastName) 
    { 
     FirstName = firstName; 
     LastName = lastName; 
    } 
    public string FirstName { get; } 
    public string LastName { get; } 
    public string Company { get; } = "Microsoft"; 
} 

var customer = new Customer("Bill", "Gates"); 

Sie mehr über Auto-Eigenschaft initializers lesen here

Verwandte Themen