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

Posted

技术标签:

【中文标题】对参数使用 IReadOnlyCollection<T> 而不是 IEnumerable<T> 以避免可能的多次枚举【英文标题】:Using IReadOnlyCollection<T> instead of IEnumerable<T> for parameters to avoid possible multiple enumeration 【发布时间】:2017-05-06 23:07:02 【问题描述】:

我的问题与this one 有关IEnumerable&lt;T&gt;IReadOnlyCollection&lt;T&gt; 的使用有关。

我也一直使用IEnumerable&lt;T&gt; 将集合公开为返回类型和参数,因为它受益于不可变和延迟执行。

但是,我越来越担心我的代码中必须枚举参数以避免 ReSharper 给出的可能的多重枚举警告的地方的扩散。我理解为什么 ReSharper 建议这样做,并且我同意它建议的代码(如下)以确保封装(即,不对调用者进行任何假设)。

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

但是,我发现此代码的重复性具有污染性,并且我同意一些消息来源,即 IReadOnlyCollection&lt;T&gt; 是更好的选择,尤其是在 this article 中提出的观点,其中指出:

最近一直在考虑回国的利弊 IEnumerable&lt;T&gt;.

从好的方面来说,它几乎是一个界面所获得的最小限度,所以它 让您作为方法作者比承诺更灵活 较重的替代方案,例如 IList&lt;T&gt; 或 (heaven forbid) 数组。

但是,正如我在 the last post 中概述的那样,IEnumerable&lt;T&gt; 返回 引诱来电者违反Liskov Substitution Principle。它太 他们很容易使用Last()Count()等LINQ扩展方法, 其语义IEnumerable&lt;T&gt; 不承诺。

需要一种更好的方法来锁定返回的集合 没有使这种诱惑如此突出。 (我想起了巴尼 法夫艰难地学习了这一课。)

输入IReadOnlyCollection&lt;T&gt;,.NET 4.5 中的新功能。它只增加了一个 IEnumerable&lt;T&gt; 的属性:Count 属性。通过承诺计数, 您向来电者保证您的IEnumerable&lt;T&gt; 确实有 终点站。然后他们可以使用 LINQ 扩展方法,例如 Last() 无愧于心。

但是,细心的人可能已经注意到,本文只讨论了使用IReadOnlyCollection&lt;T&gt; 来表示返回类型。我的问题是,相同的论点是否同样适用于将其用于参数?对此的任何理论思想或方法也将不胜感激。

事实上,如果使用IEnumerable&lt;T&gt;,我认为使用IReadOnlyCollection&lt;T&gt; 的一般经验法则是可能存在多个枚举(相对于ReSharper 警告)。否则,使用IEnumerable&lt;T&gt;

【问题讨论】:

resharper 的注释中有一个名为 [NoEnumeration] 的属性,它只是一个属性,但那里说 Indicates that IEnumerable, passed as parameter, is not enumerated. 它什么也没做,但我用它是因为它很酷 @M.kazemAkhgary,这很酷,而且很有用,我猜你没有枚举IEnumerable。但我的问题比那个用例更广泛,更多的是关于 IEnumerable 枚举的位置。在这些情况下,IReadOnlyCollection&lt;T&gt; 是更好的选择吗,出于我提到的原因(即文章中提出的观点)? 【参考方案1】:

进一步思考后,根据我在问题中提到的文章得出结论,使用IReadOnlyCollection&lt;T&gt;作为参数确实可以,但仅限于肯定会被枚举的函数.如果枚举是基于其他参数、对象状态或工作流的条件,那么它仍应作为IEnumerable&lt;T&gt; 传入,以便在语义上确保惰性求值。

【讨论】:

以上是关于对参数使用 IReadOnlyCollection<T> 而不是 IEnumerable<T> 以避免可能的多次枚举的主要内容,如果未能解决你的问题,请参考以下文章

不变性/只读语义(特别是 C# IReadOnlyCollection<T>)

IReadOnlyCollection 上的隐式/显式转换混淆

为啥泛型 ICollection 不在 .NET 4.5 中实现 IReadOnlyCollection?

有没有办法将 IReadOnlyCollection<T>/IReadOnlyList<T> 与 protobuf-net 一起使用

List<T>.AsReadOnly() 与 IReadOnlyCollection<T>

我应该使用 C# 中的哪些只读、保留顺序的集合来支持枚举?