我创建了一个函数
foo
,它创建并返回一种类型List<T>
。 ReSharper 建议我将返回类型更改为 IEnumerable<T>
。但是,我知道在调用 foo
的函数中有些人想要访问列表中的随机元素,因此需要先调用 IEnumerable<T>.ToList()
。
我应该将返回类型更改为
IEnumerable<T>
吗?为什么?
如果 Resharper 知道某些
foo
调用者想要访问列表中的随机元素,它不会建议您返回 IEnumerable<T>
。只要您拥有所有图片,您就应该遵循或不遵循 Resharper 的建议,尽管这些建议通常非常好。
有一个方法返回
List<T>
通常意味着它已经为调用者的利益构建并填充了一个新的List<T>
实例,然后调用者可以自由地修改它认为合适的集合,并且没有其他东西会修改除非调用者给出参考,否则之后收集。返回数组的方法有类似的期望。让方法返回集合接口而不是列表或数组类型通常表明它可能返回对对象的引用,该对象可能是延迟生成的或引用与其他代码共享的数据,并且需要可变集合实例的调用者也可以修改应该将它接收到的集合提供给像ToList
这样的方法,并使用后一种方法返回的集合。
如果该方法总是为了调用者的利益构造和填充一个新的
List<T>
实例,它应该使用一个返回类型来指示(即List<T>
或T[]
)。如果它可能受益于能够返回一个现有的集合或一个包装器,那么返回一些其他类型可能会更好。请注意,如果给定 ToList
以外的其他内容,调用者需要调用 List<T>
,即使返回 List<T>
的方法必须制作副本,让方法返回 List<T>
本身也是“胜利”列表的一部分,但返回其他类型的方法不会。如果无论返回类型如何,方法都必须构造一个新的集合实例,如果任何调用者最终需要调用 List<T>
作为结果,则返回 ToList
以外的任何内容都将是一种损失。
有时有用的另一种方法是让一个类提供一个方法,该方法接受
Action<T>
或类似的东西并在集合中的每个项目上调用它。想要构建一个包含来自多个集合的所有项目的列表的代码可以构建一个委托以将传入的项目附加到列表并将该委托传递给每个有问题的集合,避免需要让每个集合构建一个其项目的新List<T>
实例。
由于调用 foo 的函数会执行
IEnumerable<T>.ToList()
该函数的目的很可能不仅是遍历您的对象集合,而且是处理它(例如对其进行排序,修改某些元素, ...)。 IEnumerable 用于只需要迭代的对象集合。因此,您应该保留您的列表而不是将其更改为 IEnumerable。更多有用信息在这里
引用 ReSharper 关于代码检查的文档:返回类型可以是
IEnumerable<T>
:
如果一个方法返回一个更通用的类型,它允许更多的灵活性。一方面,如有必要,调用方方法可以将
类型的值转换为许多其他集合类型或将其与 LINQ 一起使用。另一方面,开发人员可能无需更改返回类型即可更改方法的实现。IEnumerable<T>
此外,如果您决定将返回值更改为更具体的类型,则返回更通用的类型可能会有所帮助,例如,
:如果调用者期望List<T>
,他们将能够接受IEnumerable<T>
,但反之则不然。List<T>
请注意,虽然这样的替换并不总是可行的。如果派生类型的方法在解决方案的任何位置的返回对象上使用,ReSharper 将不会发出此建议。
我的观点是,类型
IEnumerable<T>
具有强大的 延迟执行 语义,这可能会激励调用者将可枚举序列具体化为 T[]
或 List<T>
,以防他们不得不多次枚举序列。实现已经由底层数组或列表支持的序列是一种浪费,因此我强烈反对 ReSharper 的这一具体建议。我更喜欢返回一个通常不与延迟执行相关联的接口,如 IReadOnlyCollection<T>
、ICollection<T>
、IReadOnlyList<T>
或 IList<T>
.
相关的微软规则:CA1851: Possible multiple enumerations of
IEnumerable
collection.