设计一个可变类,它被消耗后变得不可变 [英] Design a mutable class that after it's consumed becomes immutable

查看:111
本文介绍了设计一个可变类,它被消耗后变得不可变的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设该场景不允许实现不可变类型。按照这样的假设,我想要关于如何正确设计一种消费后的类型的意见/示例,变得不可变。

  public class ObjectAConfig {

private int _valueB;
private string _valueA;
内部bool消耗{get;组; }

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 不可变。



例如:




  • 可以有意义地定义一个像一个< T> code>允许将包装值初始化一次?


  • 可以有意义地定义返回类型本身更改私有字段的类型吗?



解决方案

你实际执行的时间有时会以冰棒不变性 - 即你可以冻结它。您现在的方法将会工作 - 确实我在许多地方自己使用这种模式。



您可以通过以下方式减少一些重复: p>

  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()
{
返回新的ImmutableConfig(ValueA,ValueB);
}
... snip:实现ObjectAConfig
}

这里有一个真正不可变的实现 IConfig 和你原来的实现。如果您想要冻结版本,请致电 Build()


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?

解决方案

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().

这篇关于设计一个可变类,它被消耗后变得不可变的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆