对参数使用 IReadOnlyCollection<T> 而不是 IEnumerable<T> 以避免可能的多重枚举

Using IReadOnlyCollection<T> instead of IEnumerable<T> for parameters to avoid possible multiple enumeration

我的问题与 this one 有关 IEnumerable<T>IReadOnlyCollection<T> 的使用有关。

我也一直使用 IEnumerable<T> 将集合公开为 return 类型和参数,因为它受益于不可变和延迟执行。

但是,我越来越担心我的代码中必须枚举参数以避免 ReSharper 可能给出的多重枚举警告的位置的激增。我理解为什么 ReSharper 建议这样做,并且我同意它建议的代码(如下)以确保封装(即,没有关于调用者的假设)。

 Foo[] arr = col as Foo[] ?? col.ToArray();

但是,我发现此代码的重复性具有污染性,并且我同意一些消息来源认为 IReadOnlyCollection<T> 是更好的选择,尤其是 this article 中提出的观点:

Lately, I’ve been considering the merits and demerits of returning IEnumerable<T>.

On the plus side, it is about as minimal as an interface gets, so it leaves you as method author more flexibility than committing to a heavier alternative like IList<T> or (heaven forbid) an array.

However, as I outlined in the last post, an IEnumerable<T> return entices callers to violate the Liskov Substitution Principle. It’s too easy for them to use LINQ extension methods like Last() and Count(), whose semantics IEnumerable<T> does not promise.

What’s needed is a better way to lock down a returned collection without making such temptations so prominent. (I am reminded of Barney Fife learning this lesson the hard way.)

Enter IReadOnlyCollection<T>, new in .NET 4.5. It adds just one property to IEnumerable<T>: the Count property. By promising a count, you assure your callers that your IEnumerable<T> really does have a terminus. They can then use LINQ extension methods like Last() with a clear conscience.

然而,细心的人可能已经注意到,本文只讨论了将 IReadOnlyCollection<T> 用于 return 类型。我的问题是,同样的论点是否同样适用于将它用于参数?任何对此的理论想法或评论也将不胜感激。

事实上,我认为使用 IReadOnlyCollection<T> 的一般经验法则是如果 IEnumerable<T> 是可能的多重枚举(相对于 ReSharper 警告)用过的。否则,使用 IEnumerable<T>.

进一步考虑后,根据我在问题中提到的文章,我得出结论,使用 IReadOnlyCollection<T> 作为参数确实可以,但只能在它所在的函数中使用肯定会被列举。如果枚举是基于其他参数、对象状态或工作流的条件,那么它仍应作为 IEnumerable<T> 传递,以便在语义上确保惰性评估。