如何让班级真正变化?

Atu*_*eka 1 .net c# c#-3.0 c#-4.0

我刚刚研究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; } 
}
Run Code Online (Sandbox Code Playgroud)

为了使它真正只读,我必须MyProperty做为readonly.这是一个自定义类,可以修改类.如果我的列表是内置的.net类,它具有getter和setter属性怎么办?我认为在这种情况下我必须编写一个.net类的包装器,它只允许读取值.

有没有办法让现有的类不可变?

SLa*_*aks 6

你混淆了不可变只读的术语.

一个只读的对象是一个对象,不公开任何办法去改变它. ReadOnlyCollection<T>(返回AsReadOnly())是一个很好的例子).但是,IEnumerable<T>也是只读的.
重要的区别是允许只读对象发生变化.

如果你写list.Add(new Test()),你的只读集合(它只是包装list)将会改变.

只读集合对于设计安全API很有用,只允许集合的所有者更改它.
但是,它对线程安全没有任何好处.


一个不可变对象是一个对象,不能改变的一切,不管发生什么事情(反思不计). string是不可变类的一个很好的例子; string无论发生什么,现有实例的价值永远不会改变.(禁止反射,不安全代码和某些编组技巧)

.Net没有任何内置的不可变集合,但CLR团队在NuGet上发布了一个不可变集合类型库.

真正不可变的对象本质上是线程安全的.由于无法修改它们,因此其他线程不会观察到不一致的实例.


你问的是深层不变性.
如果通过其属性(整个对象图)递归访问的每个对象本身都是不可变的,那么对象就是不可变的.

除非您为需要引用的每个类创建一个不可变版本,否则不能创建一个深度不可变的对象.这些不可变版本需要从可变原始类中复制状态; 它们不能简单地包装原始实例,或者它仍然是可变的内部实例.

您可以通过在可变类周围创建只读包装来创建一个深度只读对象.


有关更多信息,请参阅我的博客.