在不可更改之前, IEnumerable 是许多API的首选接口(interface),因为它具有API对传入对象的实际类型不敏感的优点。
public void DoSomeEnumerationsWithACollection(IEnumerable<Thing> collectionOfThings)
{
foreach (var thing in collectionOfThings) doSomethingWith(thing);
foreach (var thing in collectionOfThings) doSomethingElseWith(thing);
}
当然,这至少有两个缺点:
我必须承认,除了使用命名约定外,我从未找到令人满意的模式来解决此问题。尽管存在不利方面,但务实的方法似乎是 IEnumerable 是传递收藏集的最低摩擦方法。
现在,有了不可变的集合,情况有了很大的改善。
public void DoSomeEnumerationsWithACollection(ImmutableList<Thing> collectionOfThings)
{
不再存在集合修改的风险,并且对多个枚举的性能影响也没有歧义。
但是,由于我们现在必须传递 ImmutableList ,因此显然已经失去了API的灵活性。如果我们的客户有其他种类的可枚举不可变集合,则必须将其复制到 ImmutableList 中才能使用,即使我们要做的只是枚举它。
理想情况下,我们可以使用类似
public void DoSomeEnumerationsWithACollection(IImmutableEnumerable<Thing> collectionOfThings)
但是,当然,除非约定,否则接口(interface)不能强制执行不变性之类的语义。
使用基类可能有效
public void DoSomeEnumerationsWithACollection(ImmutableEnumerableBase<Thing> collectionOfThings)
除了创建未密封的不可变类被认为是不好的形式,以免子类引入可变性。而且无论如何,BCL都没有做到这一点。
或者,我们可以继续在API中使用IEnumerable并使用命名约定来明确我们的代码依赖于要传递的不可变集合。
所以...我的问题是,在传递不可变集合时,哪种模式被认为是最好的?一旦我们开始使用不变性, ImmutableList 是“新的 IEnumerable ”还是有更好的方法?
更新资料
IReadOnlyCollection (由下面的Yuval Itzchakov建议)是对 IEnumerable 的明显改进,但仍不能完全保护消费者免受集合中不受控制的更改。值得注意的是,Roslyn代码库大量使用了不可变性(主要通过 ImmutableArray ),并且在将它们传递给其他方法时似乎使用了显式键入,尽管在几个位置中, ImmutableList 传递到了接受 IEnumerable的方法中。
最佳答案
经过几年大量使用不可变集合的经验之后,我决定在几乎所有地方都使用ImmutableArray的惯例。这并不能满足最初希望在API中提供灵活性的要求,但实际上,很少使用ImmutableList或其他类似列表的结构,并且当我这样做时,通常不需要太多开销来调用ToImmutableArray来通过接口(interface)获取数据。
关于c# - 跨API传递不可变集合的最佳模式是什么,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30274472/