5

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

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

但我有点困惑。

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

4

3 回答 3

11

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

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

此外,返回的对象与返回的对象ReadOnlyCollection相同_myDataList,即this._myDataList.First() == this.MyReadOnlyList.First()(与LINQ)。这意味着如果 in 中的对象_myDataList是可变的,那么 in 中的对象也是可变的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,这意味着返回对实际对象的引用,而不是其值的副本。

于 2015-09-07T12:42:01.547 回答
2

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

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

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

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

    public string SomeString { get; };
    public int SomeInt { get; };
}
于 2015-09-07T12:42:15.050 回答
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";
}

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

最后,结构是不可变的,但它们是值类型,不应仅仅因为它们可以表示不可变数据而使用它们。请参阅此问答以了解有关为什么结构不可变的更多信息:为什么 C# 结构不可变?

于 2015-09-07T12:42:09.053 回答