如何创建不可变的类?

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

我有一张课堂项目的清单。
不过,如果属性是只读的,则可以修改列表。

Exposing the IEnumerable of the list makes it immutable.
我想知道要使一个类成为不可变的,必须遵循的基本规则是什么?

59284 次浏览

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

您可以创建一个 readonly list 属性,如下所示:

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


}

我想你的方向是对的

  • 注入到类中的所有信息都应该在构造函数中提供
  • 所有属性应该只是 getter
  • 如果一个集合(或 Array)被传递到构造函数中,则应该复制它以防止调用方以后修改它
  • if you're going to return your collection, either return a copy or a read-only version (for example, using 数组列表,只读 or similar - you can combine this with the previous point and 商店 a read-only copy to be returned when callers access it), return an enumerator, or use some other method/property that allows read-only access into the collection
  • 请记住,如果你的任何成员是可变的,你仍然可能有一个可变类的外观-如果是这种情况,你应该复制掉任何状态,你想保留,并避免返回整个可变对象,除非你复制他们之前把他们还给调用者-另一个选择是返回可变对象的不可变的“部分”-感谢@Brian Rasmussen 鼓励我扩展这一点

此外,请记住:

public readonly object[] MyObjects;

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

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

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

Using ReadOnlyCollection will restrict client from modifying it.

The original question and answer date from the end of 2008, there is now a System.Collections.Immutable 命名空间 that I believe dates from the earliest .NET Core (1.0) . The namespace is still not available in .NET Standard (current version 2.1) and .NET framework (current version 4.8). This namespace has lots of immutable collections including the 不可变列表, which is asked about in the original question. However, I believe the System.Collections.Immutable namespace may appear in .NET 5 which is at release candidate 2 at the moment.

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

所有你需要的是 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<T>ReadOnlyCollection<T>,而不是从 System.Collections.Immutable中公开