ReadonlyCollection,对象是不可变的吗?

时间:2015-09-07 12:39:03

标签: c# readonly-collection immutable-collections

我正在尝试使用ReadOnlyCollection使对象不可变,我希望对象的属性是不可变的。

public ReadOnlyCollection<FooObject> MyReadOnlyList
{
    get
    {
        return new ReadOnlyCollection<FooObject>(_myDataList);
    }
}

但我有点困惑。

我尝试使用foreach将对象的属性更改为MyReadOnlyList并且...我可以更改value属性,是否正确?我理解ReadOnlyCollection设置了一个添加级别,使对象不可变。

3 个答案:

答案 0 :(得分:7)

ReadOnlyCollection是不可变的这一事实意味着无法修改集合,即无法在集合中添加或删除任何对象。这并不意味着它包含的对象是不可变的。

Eric Lippert的

This article解释了不同类型的不变性如何发挥作用。基本上,ReadOnlyCollection是一个不可变的外观,可以读取底层集合(_myDataList),但不能修改它。但是,您仍然可以更改基础集合,因为您通过执行_myDataList之类的操作来引用_myDataList[0] = null

此外,ReadOnlyCollection返回的对象与_myDataList返回的对象相同,即this._myDataList.First() == this.MyReadOnlyList.First()LINQ)。这意味着如果_myDataList中的对象是可变的,那么MyReadOnlyList中的对象也是如此。

如果您希望对象是不可变的,则应相应地设计它们。例如,您可以使用:

public struct Point
{
    public Point(int x, int y)
    {
        this.X = x;
        this.Y = y;
    }

    // In C#6, the "private set;" can be removed
    public int X { get; private set; }
    public int Y { get; private set; }
}

而不是:

public struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
}

编辑:在这种情况下,如Ian Goldby所述,这两个结构都不允许您修改集合中元素的属性。发生这种情况是因为结构是值类型,当您访问元素时,集合会返回值的副本。您只能修改Point类型的属性(如果它是类),这意味着将返回对实际对象的引用,而不是其值的副本。

答案 1 :(得分:2)

  

我尝试将对象的属性更改为MyReadOnlyList   使用foreach和......我可以改变价值属性,这是正确的吗?一世   了解ReadOnlyCollection设置一个添加级别来制作对象   不可变的。

使用ReadOnlyCollection不会对存储在集合中的对象做出任何保证。它保证的一切是集合一旦创建就无法修改。如果从中检索到一个元素,并且它具有可变属性,则可以很好地修改它。

如果您想让FooObject成为不可变的,那么只需这样做:

public class FooObject
{
    public FooObject(string someString, int someInt)
    {
        SomeString = someString;
        SomeInt = someInt;
    }

    public string SomeString { get; };
    public int SomeInt { get; };
}

答案 2 :(得分:1)

什么是不可变的是集合本身,而不是对象。就目前而言,C#不支持不可变对象,而不会像ReadOnlyCollection<T>那样包装它们。

好吧,如果属性没有可访问的setter,你仍然可以创建不可变对象。顺便说一句,他们根本不是一成不变的,因为他们可以从一个可能具有与设置者相同或更多可访问性的类成员中变异。

// Case 1
public class A
{
    public string Name { get; private set; }

    public void DoStuff() 
    {
        Name = "Whatever";
    }
}

// Case 2
public class A
{
    // This property will be settable unless the code accessing it
    // lives outside the assembly where A is contained...
    public string Name { get; internal set; }
}

// Case 3
public class A
{
    // This property will be settable in derived classes...
    public string Name { get; protected set; }
}

// Case 4: readonly fields is the nearest way to design an immutable object
public class A
{
     public readonly string Text = "Hello world";
}

正如我之前所说,引用类型在定义时总是可变的,并且在使用成员可访问性的某些条件下它们可以表现为不可变。

最后,结构是不可变的,但它们是值类型,它们不应该仅仅因为它们可以表示不可变数据而被使用。请参阅此问答以了解有关结构不可变的原因:Why are C# structs immutable?