2016-04-21 26 views
8

Tutto su Stack Overflow e Internet Vedo che è un buon principio di progettazione mantenere le strutture immutabili. Sfortunatamente, non vedo mai alcuna implementazione che possa effettivamente rendere queste strutture veramente immutabili.Come si rende una struttura immutabile?

Supponendo che una struttura non abbia alcun tipo di riferimento al suo interno, come posso effettivamente rendere una struttura immutabile? Cioè, come posso impedire la mutazione di uno qualsiasi dei suoi campi primitivi (forse da un'eccezione in fase di compilazione/runtime)?

ho scritto un semplice test di tentare fare una struct immutabile, ma nemmeno utilizzando il System.ComponentModel.ImmutableObjectAttribute lavorato:

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

Un po 'OT, ma c'è qualche ragione specifica per cui hai bisogno di una struttura invece di usare solo una classe? Non ho trovato un caso di utilizzo in cui una struttura è preferibile rispetto a una classe in molti anni di programmazione LOB. – StingyJack

+0

Contrassegna i campi come readonly? Inoltre, hai visto questo: http://stackoverflow.com/questions/29974301/should-a-c-sharp-struct-have-only-read-only-properties – Pawel

+0

La struttura in mente è simile alle strutture bitfield del C++. La struttura è istanziata, e il mio desiderio è che la struttura non cambierà mai - c'è una promessa che non ho modificato i dati per sbaglio. –

risposta

12

Effettuare i campi private readonly e passare l'initi al Valori nel costruttore

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; } } 
} 

A partire con C# 6.0 (Visual Studio 2015) È possibile utilizzare getter solo le proprietà

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; } 
} 

Si noti che in sola lettura campi e Getter solo le proprietà possono essere inizializzati sia nel costruttore o, in classi, anche con inizializzatori di campo o proprietà public int Field1 { get; } = 7;.

Non è garantito che il costruttore sia eseguito su una struttura. Per esempio. se si dispone di una matrice di strutture, è necessario chiamare gli inizializzatori in modo esplicito per ciascun elemento dell'array. Per gli array di tipi di riferimento, tutti gli elementi vengono inizialmente inizializzati su null, il che rende evidente che è necessario chiamare new su ciascun elemento. Ma è facile dimenticarlo per i tipi di valori come le strutture.

var immutables = new ImmutableStruct[10]; 
immutables[0] = new ImmutableStruct(5, "hello", new Person()); 
+2

Field3 è ancora considerato mutabile perché è un tipo di riferimento. È necessario restituire una copia dell'oggetto nel getter per mantenere l'istanza immutabile. – Crowcoder

+2

Oppure rendere l'oggetto di riferimento stesso immutabile. 'String' è un tipo di riferimento ma immutabile. Dal momento che le strutture rappresentano tipi di valore, avere un riferimento a un tipo di riferimento mutevole probabilmente non è una buona idea. –

+0

Bella risposta! Funziona e è aggiornato! A proposito, se si dispone di un inizializzatore di proprietà, può avvenire solo in una classe. Altrimenti si riceve l'errore: '" non può avere proprietà di istanza o inizializzatori di campo nelle strutture "'. –

3

Mantenere i vostri dati immutabili privato:

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; 
} 

o meno ingombrante:

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; } 
}