为什么我不应该总是使用ICollection而不是IEnumerable?

时间:2015-11-17 10:41:17

标签: c# linq ienumerable icollection

我在this帖子中找到了问题的解决方案 - 这让我在那里提出了新答案 - 并且 - 面对以下问题:

考虑ICollection实现IEnumerable,并且所有linq扩展都适用于这两个接口,是否有任何情况我可以通过使用IEnumerable代替ICollection而受益?

例如,非通用IEnumerable不提供Count扩展名。 两个ICollection接口都可以。

鉴于所有ICollection,在任何情况下,提供所有功能IEnumerable实施 - 因为它本身实现了它 - 为什么我会选择IEnumerable代替ICollection

与先前没有ICollection的框架的向后兼容性?

3 个答案:

答案 0 :(得分:4)

IEnumerable为集合提供只读接口,ICollection允许修改。另外IEnumerable只需知道如何迭代元素。 ICollection必须提供更多信息。

这在语义上是不同的。您并不总是希望提供修改集合的功能。

有一个IReadOnlyCollection,但它没有实现ICollection。这是C#的设计,ReadOnly是一个不同的精简界面。

蒂姆提出的观点非常重要。 Count的内部工作可能会有很大不同。 IEnumerable不需要知道它跨越了多少元素。 Collection有一个Property,所​​以它必须知道它包含多少个元素。这是另一个重要的区别。

答案 1 :(得分:4)

我认为这里有两个问题要回答。

我何时需要IEnumerable<T>

与其他集合类型和一般语言不同,IEnumerable上的查询是使用延迟评估执行的。这意味着您可以仅在枚举中执行多个相关查询。

值得注意的是,懒惰的评估与副作用没有很好的交互,因为多次枚举可能会产生不同的结果,在使用它时需要牢记这一点。在像C#这样的语言中,如果你不小心,懒惰的评估可能是一个非常强大的工具,但也是一个无法解释的行为的来源。

我什么时候不想要ICollection<T>

ICollection<T>是一个可变接口,除其他外,它还公开了添加和删除方法。除非您希望外部事物改变对象的内容,否则您不想返回它。同样,出于同样的原因,你通常不希望将其作为参数传递。

如果您确实希望集合具有明显的可变性,请务必使用ICollection<T>

此外,与IEnumerable<T>IReadOnlyCollection<T>不同,ICollection<T>不是协变,这会降低某些用例中类型的灵活性。

非通用版本

对于这些接口的非泛型版本,情况有所改变。在这种情况下,两者之间唯一真正的区别是IEnumerable提供的懒惰评估和ICollection的热切评估。

我个人倾向于避免使用非通用版本,因为在值类型的情况下缺乏类型安全性和装箱/拆箱性能差。

<强>摘要

如果您想进行延迟评估,请使用IEnumerable<T>。对于急切的评估和不变性,请使用IReadOnlyCollection<T>。对于显式可变性,请使用ICollection<T>.

答案 2 :(得分:2)

我们的想法是使用满足要求的最简单的契约(接口):ICollection是一个集合,IEnumerable是一个序列。一个序列可能有延迟执行,它可能是无限的等等。接口IEnumerable只是告诉你可以枚举序列,就是全部。这与ICollection不同,IEnumerable表示包含有限数量项目的实际集合。

如您所见,这些是完全不同的。您不能忽略这些契约的语义,只关注哪个接口继承了哪个接口。

如果您的算法只涉及枚举输入数据,那么它应该采用ICollection。如果您通过合同处理集合(即,您希望集合而不是其他内容),那么您应该使用:after