2013-02-26 80 views
5

假設該場景不允許實現不可變類型。在這個假設之後,我想就如何正確地設計一種在消費之後變得不可變的類型的意見/例子。設計一個可變類,消耗後變爲不可變

public class ObjectAConfig { 

    private int _valueB; 
    private string _valueA; 
    internal bool Consumed { get; set; } 

    public int ValueB { 
    get { return _valueB; } 
    set 
    { 
     if (Consumed) throw new InvalidOperationException(); 
     _valueB = value; 
    } 
    } 

    public string ValueA { 
    get { return _valueA; } 
    set 
    { 
     if (Consumed) throw new InvalidOperationException(); 
     _valueA = value; 
    } 
    } 
} 

ObjectA消耗ObjectAConfig

public ObjectA { 

    public ObjectA(ObjectAConfig config) { 

    _config = config; 
    _config.Consumed = true; 
    } 
} 

我不滿意,這只是工作,我想知道是否有一個更好的模式(排除在外,因爲說着,做ObjectAConfig不變的從開始設計)。

例如:

  • 可以使感限定像Once<T>一個單子在允許包裹值僅被初始化一次?

  • 可以合理的定義一個返回類型本身改變私人領域的類型?即你可以凍結它 -

+0

*爲什麼*你不能實現一個不可變的類型?如果你解釋了你正在嘗試解決的真正問題,它會有所幫助。 – 2013-02-26 12:12:02

+0

[這個問題](http://stackoverflow.com/questions/4168382)有一些有趣的模式做類似的事情。 – 2013-02-26 12:18:29

回答

10

什麼,你有時會執行名爲「popsicle immutability」下進入。你目前的方法將工作 - 事實上,我在很多地方都使用這種模式。

你可以很可能是通過類似減少一些重複:

private void SetField<T>(ref T field, T value) { 
    if (Consumed) throw new InvalidOperationException(); 
    field = value; 
} 
public int ValueB { 
    get { return _valueB; } 
    set { SetField(ref _valueB, value); } 
}  
public string ValueA { 
    get { return _valueA; } 
    set { SetField(ref _valueA, value); } 
} 

還有一個相關的方法,但:一個建設者。例如,把你現有的類:

public interface IConfig 
{ 
    string ValueA { get; } 
    int ValueB { get; } 
} 
public class ObjectAConfig : IConfig 
{ 
    private class ImmutableConfig : IConfig { 
     private readonly string valueA; 
     private readonly int valueB; 
     public ImmutableConfig(string valueA, int valueB) 
     { 
      this.valueA = valueA; 
      this.valueB = valueB; 
     } 
    } 
    public IConfig Build() 
    { 
     return new ImmutableConfig(ValueA, ValueB); 
    } 
    ... snip: implementation of ObjectAConfig 
} 

在這裏有一個真正不變的實施IConfig,和你原來的執行。如果您想要凍結版本,請致電Build()

+0

+1 @Marc Gravell,我很欣賞樣品;但我更加欣賞這種模式作爲一個具有堅實基礎概念的名稱。我也會潛入E.Lippert的文章。 – jay 2013-02-26 12:29:10

+0

對於冰棍免疫是線程安全的,所有對象設置器和凍結方法都必須使用鎖定或其他方式來確保對象在被修改時不能被凍結[如果一個對象被修改,可能會使用互鎖基元願意有不恰當的線程使用會觸發'freeze'方法中的異常]。請注意,即使一個類沒有宣告自己是線程安全的,但它應該保證,如果一個實例已經報告自己是不可變的,並且已經報告了它的狀態的任何方面,那麼這個方面將永遠不會改變。 – supercat 2013-03-12 17:47:43

+0

@supercar在許多情況下,假設某些東西會在一個孤立的區域中存在,冷凍,然後***只會暴露於多個線程是合理的。 – 2013-03-12 18:53:49