何时返回IEnumerable<T>

4

只有在需要进行延迟评估时,才应该从方法和属性中返回 IEnumerable<T>

当您返回 IEnumerableICollectionIList 时,您是否有任何模式可以遵循?


你可以在以下链接中找到答案:https://dev59.com/9HNA5IYBdhLWcg3wI6V4https://dev59.com/jnA65IYBdhLWcg3w7DT8 - Ravi Vanapalli
2
@Serge, Ravia - OP对惰性求值的标准使其与其他链接略有不同。 - StuartLC
1
@StuartLC是正确的,这个问题不应该被关闭为重复,因为那个问题并不是围绕惰性求值(延迟执行)展开的。然而,那个问题有一个答案更详细地阐述了延迟执行(https://dev59.com/9HNA5IYBdhLWcg3wI6V4#1072697)。想要了解更多有关延迟执行的信息,请参见[微软的Deferred Execution and Lazy Evaluation](https://learn.microsoft.com/en-us/dotnet/standard/linq/deferred-execution-lazy-evaluation)。 - Jonathan Van Dam
5个回答

5
我在调用方只需要迭代整个集合时,会返回IEnumerable接口。通过使用尽可能简单的接口,我确保调用代码不会过于紧密耦合,从而使以后更容易改变方法或属性中的代码。如果它仍然返回IEnumerable,则其他人无需关心。如果以前返回了List,并且新方法在内部使用了HashSet这样的东西来表示事物,那么很多其他东西也必须发生改变。
因此,我总是喜欢返回集合接口而不是具体集合,除非调用代码真的需要知道正在处理哪种类型的集合。我考虑调用方将需要执行的操作,并找到支持这些操作的最小接口。

但是返回IEnumerable<T>是一个特殊情况——它会改变返回到迭代器方法的行为——我认为这就是OP所指的。 - Rob Levine
@Matthew,如果你正在编写将被其他开发人员使用的代码,那么他们如何使用它并不清楚,他们只需要迭代还是需要知道计数等。 - Danil
Rob,是的,有时这会导致问题,但我倾向于让调用者通过急切地枚举返回来处理它(但是,我也是Haskell程序员)。Danil,我的措辞可能不太好,因为方法的作者也影响了调用者可以对其进行的操作。 - Matthew Walton

3
通常,如果是一个列表(非惰性的),我会返回IList。比如说,当我的方法预期以集合形式返回结果时,我总是这么做。 我这样做是因为这在方法签名中清晰地说明它是一个已加载的列表,而不是一个惰性评估的列表,然而使用接口并不会暴露具体类型(数组、列表、只读列表等)。
当我对传入的集合进行任何类型的转换(LINQ方法)时,我返回IEnumerable。
同时,在我的方法中,我使用IEnumerable作为参数,只是为了说明“此函数不关心,它只需要能够遍历”。当然,我确保在我的函数中只枚举IEnumerable一次。

2

如果结果只是为了您方法的调用者进行读取,那么您可以返回IEnumerable<T>


1
这个答案没有解决迭代器方法与普通方法的核心问题以及它们的优缺点。 - Rob Levine
如果我返回新的List,那么它是否只读并不重要。我的意思是,我返回的新List不是任何对象的成员。 - Danil
+1 以前从未想过这个。所以你用另一个枚举器包装你的任何类型的集合,以确保该集合真正是只读的(假设它们的元素是不可变的),而不仅仅是返回它,是吗? - Matthias Meid
另一个快速的要点 - 在没有yield关键字的情况下(即当这不是迭代器方法时),它只部分地保护返回值为只读。它仍然可以转换为基础类型,然后进行突变。我之所以假设您最初在谈论迭代器方法,是因为您提出的保护只有在迭代器方法中才真正存在。(编辑:刚刚发布此内容,看到Mudu也提出了同样的观点!) - Rob Levine
@RobLevine:正如我之前回复Mudu的那样,这是关于意图的沟通。在完全信任的环境中,你无法“保护”或“强制执行”太多东西。 - Daniel Hilgarth
显示剩余10条评论

2
在以下情况下应返回 IEnumerable<T>
  1. 结果是一个数组、列表或任何类型的集合,因为消费者只需要迭代它,所以结果并不重要。

  2. 结果使用 IEnumerable<T> 的实现进行了类型化,但公共 API 不强制消费者使用特定于 API 的类型,因此消费者使用 POCO 保持中立。

如果预计结果是一个集合,则应返回 IEnumerable<T> 的特定实现,例如 ICollection<T>IList<T>List<T> 等。这样消费者就能够使用索引器访问可枚举成员,或者修改集合本身(添加、更新、删除、搜索... 集合中的项)。

选择正确的具体类型作为属性和返回类型更多地是关于良好的案例分析,并只输入消费者需要的内容。

例如,从概念上讲,某个属性应该返回一个 list,但是消费者不应该对其进行修改,因此,该属性应该返回一个 ReadOnlyCollection<T>,但你应该将其键入为 IList<T>

正如我上面所说的,进行良好的案例分析和 保持简单


1

通常的经验法则是:

如果我确定总是知道返回结果的数量,我会返回 ICollection。如果调用者只是枚举,他们可以轻松地将其视为 IEnumerable。如果元素的顺序不是随机的,我使用 IList。在其他情况下,我使用 IEnumerable。对于一个接口的不同实现,我使用“最宽松”的接口,而不是重新复制结果以满足特定的集合接口。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接