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>允许包装值只初始化一次的monad吗?

  • 定义一个返回类型本身更改私有字段的类型是否有意义?

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 回答