1

我只是研究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设为只读。这是一个自定义类,可以修改该类。如果我的列表是具有 getter 和 setter 属性的内置 .net 类怎么办?我认为在这种情况下,我必须编写一个只允许读取值的 .net 类的包装器。

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

4

2 回答 2

6

您混淆了immutableread-only这两个术语。

只读对象是不公开任何更改它的方法的对象 。ReadOnlyCollection<T>(由AsReadOnly())返回是一个很好的例子)。但是,IEnumerable<T>也是只读的。
重要的区别是允许更改只读对象。

如果你 write list.Add(new Test()),你的只读集合(它只是 wraps list)将会改变。

只读集合对于设计安全的 API 很有用,其中只有集合的所有者可以更改它。
但是,它对线程安全没有任何好处。


不可对象是根本无法改变的对象,无论发生什么(反射不算)。 string是不可变类的一个很好的例子;string无论发生什么,现有实例的值永远不会改变。(除了反射、不安全的代码和某些编组技巧)

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

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


你问的是深度不变性。
如果通过其属性递归访问的每个对象(整个对象图)本身都是深度不可变的,则一个对象是深度不可变的。

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

相反,您可以通过围绕可变类制作只读包装器来制作深度只读对象。


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

于 2013-06-09T17:36:26.887 回答
2

真正不可变的类是其属性和字段都是只读的。这个定义是递归的,因为包含在这个类的字段和属性中的所有类也必须是只读的。您的只读列表并不是真正不可变的,因为列表的内容不是不可变的,因为正如您所说,您可以修改它们。列表中的各个项目也必须是只读的,IReadOnlyList<T>才能真正不可变。

于 2013-06-09T17:37:36.263 回答