如何创建不可变类?

时间:2008-12-09 11:39:25

标签: c# .net immutability

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

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

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

8 个答案:

答案 0 :(得分:113)

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

  • 注入到类中的所有信息都应该在构造函数中提供
  • 所有属性应仅为getters
  • 如果将一个集合(或数组)传递给构造函数,则应该将其复制以防止调用者稍后修改它
  • 如果您要归还收藏品,请返回副本或只读版本(例如,使用ArrayList.ReadOnly或类似版本) - 您可以将其与之前的点和商店<结合使用/ em>当调用者访问它时返回的只读副本),返回枚举器,或使用允许对集合进行只读访问的其他方法/属性
  • 请记住,如果您的任何成员都是可变的,您仍然可能具有可变类的外观 - 如果是这种情况,您应该复制掉您想要保留的任何状态并避免返回整个可变对象,除非你复制它们然后再将它们交还给调用者 - 另一种选择是只返回可变对象的不可变“部分” - 感谢@Brian Rasmussen鼓励我扩展这一点

答案 1 :(得分:17)

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

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

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

}

答案 2 :(得分:10)

另外,请记住:

public readonly object[] MyObjects;

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

答案 3 :(得分:4)

使用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; }
}

答案 4 :(得分:2)

您只需要L ... Ehm record C#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";

这被转换为具有三个属性FirstNameLastNameItems的不变类,称为Customer。

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

答案 5 :(得分:1)

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

答案 6 :(得分:0)

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

答案 7 :(得分:0)

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

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