【问题标题】:How do I create an immutable Class?如何创建不可变类?
【发布时间】:2010-09-26 00:46:50
【问题描述】:

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

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

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

【问题讨论】:

  • 我强烈建议您阅读Eric Lippert's blog series on immutability,尤其是"kinds of immutability" 上的条目。您对“暴露列表的 IEnumerable 使其不可变”的评论对我来说似乎有些奇怪。你这是什么意思?
  • 我的意思是,允许用户使用 IEnumerable 访问成员,而不是允许访问列表(如果对象有其他对象的列表)。这里的谈话列表就像一个具体的例子,但它可以是任何数据结构。
  • Jon Skeet 与 Eric Lippert 的博客系列的链接在 MSDN 归档这些博客时已断开。请参阅"kinds of immutability"。该系列的其余部分似乎在 2007 年 12 月 + 2008 年 1 月的左侧面板中。
  • 请参阅 Eric Lippert 的博客系列,了解人们通常如何混淆 atomicityvolatilityimmutabilityPart OnePart TwoPart Three 等术语。这些来自他的个人博客,我相信,比他的 MSDN 帖子更适合新手。

标签: c# .net immutability


【解决方案1】:

您只需要 L...Ehm recordC# 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 的不可变类,具有三个属性 FirstNameLastNameItems

如果您需要一个不可变(只读)集合作为类的属性,最好将其公开为IEnumerable&lt;T&gt;ReadOnlyCollection&lt;T&gt;,而不是来自System.Collections.Immutable 的内容

【讨论】:

    【解决方案2】:

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

    此外,从 C# 6 开始,您可以仅使用 { get; 来获得不可变的自动实现属性。 } .

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案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; }
        }
        

        【讨论】:

          【解决方案5】:

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

          【讨论】:

            【解决方案6】:

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

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

            【讨论】:

            • 我应该编写一个包装器查找属性,它只允许您进行查找吗?并感谢您的回答。
            • 任何作为参数传递给构造函数的可变引用类型都应该被复制。否则调用者仍将持有对状态的引用。
            • @Brian Rasmussen,你是对的,但即使它被复制,任何调用者都可能访问可变对象,具体取决于提供的 acces.sors。在传递可变对象的情况下,该类最好总是返回不同的副本或对象的不可变部分
            • @Blair - 如果我在课堂上有字典,我只想用它来查找。进行查找的只读属性应该没问题吗?
            • @Biswanath - 是的,需要注意的是,如果字典中的值是可变的,如果您不希望它们发生突变,则需要在返回之前保护它们
            【解决方案7】:

            另外,请记住:

            public readonly object[] MyObjects;
            

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

            【讨论】:

              【解决方案8】:

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

              您可以按如下方式制作只读列表属性:

              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
              
              }
              

              【讨论】:

              • 我觉得 ImmutableList 会更好。
              • 使用ImmutableList,也可以考虑封类
              • 我不相信 ImmutableList 非常适合这个用例:basic rules to make a class (that contains a list) immutable。在将项目添加到列表副本时,ImmutableList 语义可以更有效地使用内存,但在我看来,这是一个更具体的用例。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2022-11-23
              相关资源
              最近更新 更多