Design a mutable class that after it's consumed becomes immutable

倖福魔咒の 提交于 2019-12-06 03:33:16

问题


Suppose that the scenario doesn't allow to implement an immutable type. Following that assumption, I'd like opinions / examples on how to properly design a type that after it's consumed, becomes immutable.

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

When ObjectA consumes ObjectAConfig:

public ObjectA {

  public ObjectA(ObjectAConfig config) {

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

I'm not satisfied that this simply works, I'd like to know if there's a better pattern (excluded, as said, making ObjectAConfig immutable by design from begin).

For example:

  • can make sense define a monad like Once<T> that allow the wrapped value to be initialized only once?

  • can make sense define a type that returns the type itself changing a private field?


回答1:


What you are implementing sometimes goes under the name "popsicle immutability" - i.e. you can freeze it. Your current approach will work - indeed I use that pattern myself in numerous places.

You can probably reduce some duplication via something like:

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

There is another related approach, though: a builder. For example, taking your existing class:

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
}

Here there is a truly immutable implementation of IConfig, and your original implementation. If you want the frozen version, call Build().



来源:https://stackoverflow.com/questions/15088773/design-a-mutable-class-that-after-its-consumed-becomes-immutable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!