如何让班级真正变化?

时间:2013-06-09 17:33:15

标签: c# .net c#-4.0 c#-3.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; } 
}

为了使其真正只读,我必须将MyProperty作为readonly。这是一个自定义类,可以修改类。如果我的列表是内置的.net类,它同时具有getter和amp;二传特性?我认为在这种情况下我必须编写一个.net类的包装器,它只允许读取值。

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

2 个答案:

答案 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>才能真正变为不可变。