我只是查看了IReadOnlyList<T>
来创建只读列表。但我认为这不是100%只读。我无法在列表中添加/删除项目,但我仍然可以修改成员。
考虑这个例子。
class Program
{
static void Main(string[] args)
{
List<Test> list = new List<Test>();
list.Add(new Test() { MyProperty = 10 });
list.Add(new Test() { MyProperty = 20 });
IReadOnlyList<Test> myImmutableObj = list.AsReadOnly();
// I can modify the property which is part of read only list
myImmutableObj[0].MyProperty = 30;
}
}
public class Test
{
public int MyProperty { get; set; }
}
为了使其真正只读,我必须将MyProperty
作为readonly。这是一个自定义类,可以修改类。如果我的列表是内置的.net类,它同时具有getter和amp;二传特性?我认为在这种情况下我必须编写一个.net类的包装器,它只允许读取值。
有没有办法让现有的类不可变?
答案 0 :(得分:6)
您会混淆不可变和只读这两个词。
只读对象是一个不公开任何改变方法的对象。 ReadOnlyCollection<T>
(由AsReadOnly()
返回)是一个很好的例子)。但是,IEnumerable<T>
也是只读的
重要的区别是允许只读对象发生变化。
如果你写list.Add(new Test())
,你的只读集合(只包装list
)就会改变。
只读集合对于设计安全API很有用,只允许集合的所有者更改它 但是,它对线程安全没有任何好处。
不可变对象是一个无法更改 的对象,无论发生什么(Reflection都不计算)。 string
是不可变类的一个很好的例子;无论发生什么,现有string
实例的值永远不会改变。 (禁止反射,不安全的代码和某些编组技巧)
.Net没有任何内置的不可变集合,但是CLR团队released a library of immutable collection types on NuGet。
真正不可变的对象本质上是线程安全的。由于无法修改它们,因此其他线程不会观察到不一致的实例。
你问的是深度不变性 如果通过其属性(整个对象图)递归访问的每个对象本身都是不可变的,那么对象就是不可变的。
除非您为需要引用的每个类创建一个不可变版本,否则不能创建一个深度不可变的对象。这些不可变版本需要从可变原始类中复制状态;它们不能简单地包装原始实例,或者它仍然可以通过内部实例进行改变。
您可以通过在可变类周围创建只读包装来创建一个深度只读对象。
有关详细信息,请参阅my blog。
答案 1 :(得分:2)
真正不可变的类是其属性和字段都是只读的类。此定义是递归的,因为此类的字段和属性中包含的所有类也必须是只读的。您的只读列表不是真正不可变的,因为列表的内容不是不可变的,因为您说可以修改它们。列表中的各个项目也必须是只读的,IReadOnlyList<T>
才能真正变为不可变。