123

我正在创建一个不可变的类。
我已将所有属性标记为只读。

我有一个班级项目清单。
虽然如果属性是只读的,则可以修改列表。

公开列表的 IEnumerable 使其不可变。
我想知道使类不可变必须遵循的基本规则是什么?

4

8 回答 8

129

我认为你在正确的轨道上-

  • 注入类的所有信息都应在构造函数中提供
  • 所有属性都应该只是吸气剂
  • 如果将集合(或数组)传递给构造函数,则应将其复制以防止调用者稍后对其进行修改
  • 如果你要返回你的集合,要么返回一个副本,要么返回一个只读版本(例如,使用ArrayList.ReadOnly或类似的 - 你可以将它与前一点结合起来并存储一个只读副本以在何时返回调用者访问它),返回一个枚举器,或使用其他一些允许只读访问集合的方法/属性
  • 请记住,如果您的任何成员是可变的,您仍然可能具有可变类的外观 - 如果是这种情况,您应该复制您想要保留的任何状态并避免返回整个可变对象,除非您复制它们在将它们返回给调用者之前 - 另一种选择是只返回可变对象的不可变“部分” - 感谢@Brian Rasmussen 鼓励我扩展这一点
于 2008-12-09T11:47:59.027 回答
21

为了不可变,您的所有属性和字段都应该是只读的。任何列表中的项目本身都应该是不可变的。

您可以按如下方式创建只读列表属性:

public class MyClass
{
    public MyClass(..., IList<MyType> items)
    {
        ...
        _myReadOnlyList = new List<MyType>(items).AsReadOnly();
    }

    public IList<MyType> MyReadOnlyList
    {
        get { return _myReadOnlyList; }
    }
    private IList<MyType> _myReadOnlyList

}
于 2008-12-09T11:47:33.927 回答
12

另外,请记住:

public readonly object[] MyObjects;

即使用 readonly 关键字标记也不是不可变的。您仍然可以通过索引访问器更改单个数组引用/值。

于 2008-12-09T11:57:03.580 回答
5

使用ReadOnlyCollection类。它位于System.Collections.ObjectModel命名空间中。

在返回列表(或在构造函数中)的任何内容上,将列表设置为只读集合。

using System.Collections.ObjectModel;

...

public MyClass(..., List<ListItemType> theList, ...)
{
    ...
    this.myListItemCollection= theList.AsReadOnly();
    ...
}

public ReadOnlyCollection<ListItemType> ListItems
{
     get { return this.myListItemCollection; }
}
于 2008-12-09T13:18:33.367 回答
3

您只需要 L...EhmrecordC# 9.0或更高版本。

public record Customer(string FirstName, string LastName, IEnumerable<string> Items);

//...

var person = new Customer("Test", "test", new List<string>() { "Test1", "Test2", "Test3" });
// you can't change anything within person variable
// person.FirstName = "NewName";

这将被转换为名为 Customer 的不可变类,具有三个FirstName属性LastNameItems

如果您需要一个不可变(只读)集合作为类的属性,最好将其IEnumerable<T>公开ReadOnlyCollection<T>System.Collections.Immutable

于 2020-11-11T21:02:55.190 回答
2

另一种选择是使用访问者模式,而不是暴露任何内部集合。

于 2008-12-09T12:38:20.047 回答
1

使用ReadOnlyCollection将限制客户端对其进行修改。

于 2019-11-12T12:36:55.680 回答
0

最初的问答日期是 2008 年底,现在有一个System.Collections.Immutable 命名空间,我相信它可以追溯到最早的 .NET Core (1.0) 。命名空间在 .NET Standard(当前版本 2.1)和 .NET Framework(当前版本 4.8)中仍然不可用。这个命名空间有很多不可变的集合,包括在原始问题中询问的ImmutableList 。但是,我相信 System.Collections.Immutable 命名空间可能会出现在 .NET 5 中,而 .NET 5 目前处于候选版本 2 中。

此外,从 C# 6 开始,您只需使用{ get;就可以拥有不可变的自动实现属性。}

于 2020-11-05T20:57:09.027 回答