最佳实践:如何公开只读 ICollection
-
08-07-2019 - |
题
我有一个 ICollection<T>
被称为 foos
在我的班级中,我想将其公开为只读(请参阅 这个问题)。我看到接口定义了一个属性 .IsReadOnly
, ,这似乎合适...我的问题是这样的:我如何让该课程的消费者清楚地知道 foos
是只读的吗?
我不想依赖他们记得查询 .IsReadOnly
在尝试未实现的方法之前,例如 .Add()
. 。理想情况下,我想揭露 foos
作为一个 ReadOnlyCollection<T>
, ,但它没有实现 IList<T>
. 。我应该暴露吗 foo
通过一个名为的方法,例如 GetReadOnlyFooCollection
而不是通过财产?如果是这样,这不会让那些期待着的人感到困惑吗? ReadOnlyCollection<T>
?
这是 C# 2.0,所以扩展方法如 ToList()
不可用...
解决方案
我似乎已经决定返回 IEnumerable 与克隆的对象。
public IEnumerable<Foose> GetFooseList() {
foreach(var foos in Collection) {
yield return foos.Clone();
}
}
- 需要 Foos 上的 Clone 方法。
这不允许对集合进行任何更改。请记住,ReadonlyCollection 是“泄漏的”,因为它内部的对象可以更改,如另一篇文章中的链接中所述。
其他提示
你可以使<!>“; foos <!>”;像这样的ReadOnlyCollection:
ReadOnlyCollection<T> readOnlyCollection = foos.ToList<T>().AsReadOnly();
然后,您可以将其作为班级的属性公开。
编辑:
class FooContainer
{
private ICollection<Foo> foos;
public ReadOnlyCollection<Foo> ReadOnlyFoos { get { return foos.ToList<Foo>().AsReadOnly();} }
}
注意:你应该记住,一旦你获得了ReadOnlyFoos集合,就不再是<!> quot; synchronized <!> quot;与您的foos ICollection。请参阅您引用的主题
自编写问题以来,.NET 4.0添加了一个IReadOnlyCollection<T>
接口;将它用作声明的返回类型可能会很好。
List<T>
,则第三个将返回原始集合。在某些情况下,每种方法都是最好的方法,但在其他方法中则不太理想(或者可能是彻头彻尾的可怕)。不幸的是,微软没有提供标准的方法来提出一个问题,即两个非常重要的问题:
-
您是否承诺永远和永远包含与您现在相同的项目?
-
您是否可以安全地直接暴露于不应修改内容的代码。
醇>
-
对象是由客户端识别为不可变的类型提供的,但是使用客户端不识别为不可变的类型而不是直接返回它。因此,客户被迫再次复制该集合。
-
一个对象是由客户端认为是不可变的类型提供的,但在返回之前它被包装的方式使得客户端无法判断该集合是否是不可变的,因此是被迫复制。
-
不应该变异的可变类型的对象由客户端提供(强制转换为只读接口)。然后将其直接暴露给另一个客户端,该客户端确定它是一个可变类型并继续修改它。
-
接收对可变集合的引用,并在返回到需要不可变对象的客户端之前封装在只读包装器中。返回集合的方法承诺它是不可变的,因此客户拒绝制作自己的防御副本。然后客户准备不足以收集该集合的可能性。
醇>
哪种样式的包装是合适的取决于客户端代码对它收到的东西的期望。有些情况需要避免:
实际上并没有特别的<!>“安全<!>”;对象可以采用从一些客户端收到的集合并且需要向其他客户端公开的操作。在许多情况下,始终复制所有内容都是最安全的操作过程,但它很容易导致这样一种情况:不需要复制的集合最终会被复制数百或数千次。返回的引用通常是最有效的方法,但它也可能在语义上很危险。
我希望微软能够添加一种标准方法,通过该方法可以询问上述问题的集合,或者更好的是,要求它们生成当前状态的不可变快照。不可变集合可以非常便宜地返回其当前状态的不可变快照(只返回自身),甚至一些可变集合类型可以以远低于完整枚举成本的成本返回不可变快照(例如,T[][]
可能支持由两个ICloneable
数组组成,其中一个数组保存对256个项目的可共享不可变数组的引用,另一个数组保存对256个项目的不可修改的可变数组的引用。制作列表的快照只需要克隆包含项目的内部数组自上次快照以来已被修改过 - 可能比克隆整个列表要便宜得多。不幸的是,因为没有标准的<!>来创建一个不可变的快照<!>接口[注意<=>不计算因为可变列表的克隆是可变的;而可以通过封装可变克隆来创建不可变快照在只读包装器中,只能用于可克隆的东西,甚至不可克隆的类型仍应支持<!>“可变快照<!>”;功能。]
我的建议是返回使用ReadOnlyCollection <!> lt; T <!> gt;对于场景直接。这使得用法对调用用户显而易见。
通常我会建议使用适当的界面。但鉴于.NET Framework目前没有合适的IReadOnlyCollection,您必须使用ReadOnlyCollection类型。
使用ReadOnlyCollection时也必须注意,因为它实际上不是只读的: Immutability and ReadOnlyCollection
有时您可能想要使用界面,可能是因为您想在单元测试期间模拟集合。请参阅我的博客条目,了解如何使用自己的界面向ReadonlyCollection添加适配器。
我通常会返回 IEnumerable&lt; T&gt;
。
一旦你只收集了一个集合(所以像添加
,删除
和 Clear
这样的方法不再有用),那么集合就没那么多了支持一个可枚举的东西 - 只是 Count
和 Contains
,我相信。
如果你班级的消费者确实需要处理他们在集合中的元素,那么很容易将 IEnumerable
传递给 List&lt; T&gt;
的构造函数
返回T []:
private ICollection<T> items;
public T[] Items
{
get { return new List<T>(items).ToArray(); }
}