将对象转换为不可变对象

本文关键字:对象 不可变 转换 | 更新日期: 2023-09-27 18:11:20

我最初创建了一个不是不可变的类,但是现在我想要有一个创建不可变等效数据结构的选项。例如,假设有一个可变类:

namespace Utility
{
    public class bar
    {
        public string name { get; set; }
        public double weight { get; set; }
        public int age { get; set;}
        public List<...> friendInstances; //instantiated at run time
    }
}
  //and now I make a mutable class.
public class MemorySafe_bar
{
    private readonly string _name;
    private readonly double _weight;
    private readonly int _age;
    private readonly List<...> _friendInstances;
    public MemorySafe_bar(string name, double weight, int age,
         List<...> friend Inst)
    {
        _name = name;
        _weight = weight;
        _age = age;
        _friendInstances = Inst
    }
    //..getters would go here...
    function()
    {
      Utility.bar bar_ex = new bar();
      bar_ex.name = "Kathy";
      bar_ex.weight = 42.34;
      bar_ex.age = 10;
      bar_ex.List<...> friends = new List<...>();
      friends.Add(stuff);
      Utility.MemorySafe_bar = new MemorySafe_bar(
        bar_ex.name, bar_ex.weight, bar_ex.age, friends);
    }
}

我不相信从现在开始可变对象将来会被改变。

将对象转换为不可变对象

如果你想要一个通用的/可重用的方法来包装任何类成为一个不可变的版本,这在一般意义上是不可能的。

如果一个特定的类将其成员公开为virtualabstract(或作为interface),则可以创建对setter不做任何操作(或抛出异常)的实现,但这通常是意想不到的。

在您当前的情况下,我将首先更新构造函数以接受您正在包装的对象,或者通过静态工厂方法这样做。我还将存储friendInstances的本地副本并返回它的只读枚举:

public class ReadOnlyBar
{
    public string name { get; private set; }
    public double weight { get; private set; }
    public int age { get; private set; }
    private readonly Friend[] _friendInstances;
    public IEnumerable<Friend> friendInstances
    {
        get
        {
            foreach(var friend in _friendInstances)
                yield return friend;
        }
    }
    public ReadOnlyBar(Bar bar)
    {
        this.name = bar.name;
        this.weight = bar.weight;
        this.age = bar.age;
        this._friendInstances = bar.friendInstances.ToArray();
    }
}
使用

:

Bar mutableBar = new mutableBar() { name="Kathy", .... };
ReadOnlyBar readonlyBar = new ReadOnlyBar(mutableBar);

我只保留不可变栏使用属性而不是readonly字段,只是为了尽可能地匹配原始Bar的API;这些可以很容易地切换回字段(这将有助于对类中愚蠢的代码实施不变性)。您还可以轻松地将创建移动到静态工厂方法或扩展方法中,这样您可能会得到如下用法:

Bar mutableBar = new mutableBar() { name="Kathy", .... };
ReadOnlyBar readonlyBar = ReadOnlyBar.Create(mutableBar);
//or
ReadOnlyBar readonlyBar = mutableBar.MakeReadOnly();

编辑:如果你想保持List<Friend>的大部分功能/成员,而不将其降级为IEnumerable,另一个快速的选择,你可以使用这个代替:

public ReadOnlyCollection<Friend> friendInstances { get; private set; }
public ReadOnlyBar(Bar bar)
{
    //other initialization
    this.friendInstances = bar.friendInstances.ToList().AsReadOnly();
}

或者你甚至可以输入is为List<Friend>,在getter中返回内部列表的副本,但这可能有点过分了,并且在"不可变"对象类型上暴露是一个令人困惑的属性。