ReadonlyCollection,是不可变的对象

本文关键字:对象 不可变 ReadonlyCollection | 更新日期: 2023-09-27 18:11:28

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

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

但是我有点困惑。

我试图改变对象的属性在MyReadOnlyList使用foreach和…我可以改变值属性,它是正确的吗?我理解ReadOnlyCollection设置了一个add级别,使对象不可变。

ReadonlyCollection,是不可变的对象

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

Eric Lippert的这篇文章解释了不同类型的不变性是如何工作的。基本上,ReadOnlyCollection是一个不可变的facade,它可以读取底层集合(_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所指出的,这两个struct都不允许修改集合中元素的属性。这是因为结构体是值类型,当您访问一个元素时,集合返回该值的副本。您只能修改Point类型的属性,如果它是一个类,这将意味着返回对实际对象的引用,而不是其值的副本。

我试图将对象的属性更改为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; };
}

不可变的是集合本身,而不是对象。目前,c#不支持不可变对象,除非像ReadOnlyCollection<T>那样对它们进行包装。

你仍然可以创建不可变对象,如果它们的属性没有可访问的setter。顺便说一句,它们根本不是不可变的,因为它们可以从具有与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";
}

正如我前面说过的,引用类型在定义上总是可变的,在某些条件下,它们可以表现为不可变的。

最后,结构体是不可变的,但它们是值类型,不应该仅仅因为它们可以表示不可变数据而使用它们。关于为什么结构是不可变的,请参阅本问答:为什么c#结构是不可变的?