2013-02-26 5 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; 
    } 
    } 
} 

ObjectAObjectAConfig消費:言ったよう

public ObjectA { 

    public ObjectA(ObjectAConfig config) { 

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

が、私は、これは単純に動作することを満足していないが、私はによってObjectAConfigは不変作り、より良いパターン(除外ありますかどうかを知りたいのですがデザインは開始から)。例えば

  • は意味が包まれた値が一度だけ初期化することを可能にするOnce<T>のようなモナドを定義することができますか?

  • プライベートフィールドを変更する型自体を返す型を定義する意味がありますか?

+0

*なぜ*あなたは不変型を実装できませんか?あなたが解決しようとしている実際の問題を説明したなら、それは助けになりました。 –

+0

[この質問](http://stackoverflow.com/questions/4168382)には、同様のことを行う面白いパターンがいくつかあります。 –

答えて

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

+0

スレッドセーフであるためには、すべてのオブジェクトセッターとフリーズメソッドは、オブジェクトが変更されている間、オブジェクトをフリーズできないようにするために、ロックなどの手段を使用する必要があります。不正なスレッドの使用が 'freeze'メソッドで例外を引き起こすことを喜んでします。クラスがスレッドセーフであると宣言していなくても、インスタンスが自身を不変であると報告し、その状態のいずれかの側面を報告した場合、その部分は決して変更されないことを保証しなければならない。 – supercat

+0

@supercar多くの場合、何かが孤立した領域に凍結され、***が複数のスレッドに公開されていると仮定するのが妥当です。 –

関連する問題