2016-04-21 10 views
8

Over Stack Overflow und das Internet Ich sehe, dass es ein gutes Designprinzip ist, um Strukturen unveränderbar zu halten. Leider sehe ich nie eine Implementierung, die diese Strukturen wirklich unveränderlich macht.Wie mache ich eine Struktur unveränderlich?

Angenommen, eine Struktur hat keine Referenztypen darin, wie mache ich eine Struktur eigentlich unveränderlich? Das heißt, wie verhindere ich die Mutation eines seiner primitiven Felder (vielleicht durch eine Kompilierungszeit/Laufzeitausnahme)?

Ich schrieb einen einfachen Test eine Struktur unveränderlich Versuch machen, aber nicht einmal mit der System.ComponentModel.ImmutableObjectAttribute gearbeitet:

class Program 
{ 
    static void Main(string[] args) 
    { 
     ImmutableStruct immStruct1 = new ImmutableStruct(); 
     Console.WriteLine(immStruct1); //Before mutation. 

     immStruct1.field1 = 1; 
     immStruct1.field2 = "Hello"; 
     immStruct1.field3 = new object(); 
     Console.WriteLine(immStruct1); //After 1st mutation. 

     immStruct1.field1 = 2; 
     immStruct1.field2 = "World"; 
     immStruct1.field3 = new object(); 
     Console.WriteLine(immStruct1); //After 2nd mutation. 

     Console.ReadKey(); 
    } 
} 

[ImmutableObject(true)] 
struct ImmutableStruct 
{ 
    public int field1; 
    public string field2; 
    public object field3; 

    public override string ToString() 
    { 
     string field3String = "null"; 
     if (field3 != null) 
     { 
      field3String = field3.GetHashCode().ToString(); 
     } 
     return String.Format("Field1: {0}, Field2: {1}, Field3: {2}", field1, field2, field3String); 
    } 
} 
+0

Ein bisschen OT, aber gibt es einen bestimmten Grund brauchen Sie eine Struktur statt nur mit einer Klasse? Ich habe keinen Anwendungsfall gefunden, in dem eine Struktur in vielen Jahren der LOB-Programmierung einer Klasse vorzuziehen ist. – StingyJack

+0

Felder als schreibgeschützt markieren? Haben Sie auch Folgendes gesehen: http://stackoverflow.com/questions/29974301/should-a-c-sharp-struct-have-only-read-only-properties – Pawel

+0

Die Struktur im Hinterkopf ähnelt der Bitfeldstruktur von C++. Die Struktur wird instanziiert, und mein Wunsch ist, dass die Struktur niemals geändert wird - es gibt ein Versprechen, dass ich die Daten nicht versehentlich geändert habe. –

Antwort

12

Machen Sie die Felder private readonly und übergeben Sie die Initi al-Werte im Konstruktor

public struct ImmutableStruct 
{ 
    private readonly int _field1; 
    private readonly string _field2; 
    private readonly object _field3; 

    public ImmutableStruct(int f1, string f2, object f3) 
    { 
     _field1 = f1; 
     _field2 = f2; 
     _field3 = f3; 
    } 

    public int Field1 { get { return _field1; } } 
    public string Field2 { get { return _field2; } } 
    public object Field3 { get { return _field3; } } 
} 

Beginnend mit C# 6.0 (Visual Studio 2015) Sie Eigenschaften Getter nur

public struct ImmutableStruct 
{ 
    public ImmutableStruct(int f1, string f2, object f3) 
    { 
     Field1 = f1; 
     Field2 = f2; 
     Field3 = f3; 
    } 

    public int Field1 { get; } 
    public string Field2 { get; } 
    public object Field3 { get; } 
} 

Beachten Sie, dass Felder schreibgeschützt und nur Eigenschaften initialisiert entweder im Konstruktor werden Getter verwenden können oder, in Klassen, auch mit Feld- oder Eigenschaftsinitialisierern public int Field1 { get; } = 7;.

Es ist nicht garantiert, dass der Konstruktor auf einer Struktur ausgeführt wird. Z.B. Wenn Sie ein Array von Strukturen haben, müssen Sie die Initialisierer dann für jedes Array-Element explizit aufrufen. Für Arrays von Referenztypen werden alle Elemente zuerst auf null initialisiert, was es offensichtlich macht, dass Sie new für jedes Element aufrufen müssen. Aber es ist leicht, es für Werttypen wie Strukturen zu vergessen.

+2

Field3 wird immer noch als veränderbar betrachtet, da es sich um einen Referenztyp handelt. Sie müssen eine Kopie des Objekts im Getter zurückgeben, um die Instanz unveränderbar zu halten. – Crowcoder

+2

Oder machen Sie das referenzierte Objekt selbst unveränderlich. 'String' ist ein Referenztyp, aber unveränderlich. Da Strukturen Werttypen darstellen, ist es wahrscheinlich auch keine gute Idee, einen Verweis auf einen veränderbaren Referenztyp zu haben. –

+0

Schöne Antwort! Es funktioniert und ist auf dem neuesten Stand! Übrigens, wenn Sie einen Eigenschaftsinitialisierer haben, kann er nur in einer Klasse vorkommen. Andernfalls erhalten Sie den Fehler: '" kann keine Instanzeigenschaft oder Feldinitialisierer in Strukturen haben "'. –

3

Halten Sie Ihre unveränderlichen Daten privat:

struct ImmutableStruct 
{ 
    private int field1; 
    private string field2; 
    private object field3; 

    public ImmutableStruct(int f1, string f2, object f3) 
    { 
     field1 = f1; 
     field2 = f2; 
     field3 = f3; 
    } 

    public int Field1 => field1; 
    public string Field2 => field2; 
    public object Field3 => field3; 
} 

oder weniger überladen:

struct ImmutableStruct 
{ 
    public ImmutableStruct(int f1, string f2, object f3) 
    { 
     Field1 = f1; 
     Field2 = f2; 
     Field3 = f3; 
    } 

    public int Field1 { get; } 
    public string Field2 { get; } 
    public object Field3 { get; } 
}