我最初创建的类不是不可变的,但现在我想要选择创建一个不可变的等效数据结构。例如,假装可变类:
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);
}
}
我不相信将来可以改变这个可变对象。
答案 0 :(得分:2)
如果你要求一个通用/可重用的方法将任何类包装成一个不可变版本,那么从一般意义上来说它实际上是不可能的。
如果特定类将其成员公开为virtual
或abstract
(或interface
),则可以创建不执行任何操作的实现(或抛出异常) )在制定者身上,但这通常是出乎意料的。
在你目前的情况下,我会首先更新构造函数以获取要包装的对象,或者通过静态工厂方法执行此操作。我还会存储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();
}
或者您甚至可以键入List<Friend>
并在getter中返回内部列表的副本,但这可能会有点远,并且是一个令人困惑的属性“不可变”的对象类型。