ReadonlyCollection,对象是不可变的吗?

ReadonlyCollection, are the objects immutable?

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

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

但我有点困惑。

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

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

Eric Lippert 的

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

之类的操作来引用 _myDataList

此外,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 所述,两个结构都不允许您修改集合中元素的属性。发生这种情况是因为结构是值类型,当您访问元素时,集合 returns 是该值的副本。您只能修改 Point 类型的属性,如果它是 class,这意味着返回对实际对象的引用,而不是它们值的副本。

不可变的是集合本身,而不是对象。目前,C# 不支持不包装不可变对象,就像 ReadOnlyCollection<T> 在您的情况下所做的那样。

好吧,如果对象的属性不可访问,您仍然可以创建不可变对象 setter。顺便说一句,它们根本不是不可变的,因为它们可以从 class 成员变异,该成员可能具有与 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?

I tried to change the property of the object in to MyReadOnlyList using a foreach and ... I can change value property, is it correct? I understood ReadOnlyCollection set an add level to make the object immutable.

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

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

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

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