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>ラップされた値を一度だけ初期化できるようなモナドを定義するのは理にかなっていますか?

  • プライベートフィールドを変更するタイプ自体を返すタイプを定義するのは理にかなっていますか?

4

1 に答える 1

10

あなたが実装しているものは、「アイスキャンディーの不変性」という名前で呼ばれることがあります-つまり、フリーズできます。あなたの現在のアプローチは機能します-実際、私はそのパターンを自分で多くの場所で使用しています.

おそらく、次のような方法で重複を減らすことができます。

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()ください。

于 2013-02-26T12:17:52.627 に答える