我创建了一个函数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>
的方法必须复制列表,但返回其他类型的方法不会。 如果方法必须构造一个新的集合实例而不考虑返回类型,如果任何调用方最终需要调用ToList
,则返回除List<T>
以外的任何内容都将是一种损失。
有时有用的另一种方法是让类提供一个接受Action<T>
或类似内容的方法,并在集合中的每个项上调用它。 想要构建包含来自多个集合的所有项的列表的代码可以构造一个委托,以将传入的项追加到列表中,并将该委托传递给每个有问题的集合,从而避免需要让每个集合为其项构建一个新的List<T>
实例。
由于调用foo的函数会执行IEnumerable<T>.ToList()
因此该函数的目的很可能不仅是遍历对象集合,而且还是处理它(例如对其进行排序,修改某些元素,...(。IEnumerable 用于只需要迭代的对象集合。因此,您应该保留列表,而不是将其更改为 IEnumerable。更多有用的信息在这里
引用 ReSharper 关于代码检查的文档: 返回类型可以IEnumerable<T>
:
如果方法返回更泛型的类型,则允许更大的灵活性。一方面,调用方方法可以在必要时将
IEnumerable<T>
类型的值转换为许多其他集合类型,或将其与 LINQ 一起使用。另一方面,开发人员可能能够更改方法的实现,而无需更改返回类型。此外,如果您决定将返回值更改为更具体的类型,则返回更通用的类型可能会有所帮助,例如,
List<T>
:如果调用者期望IEnumerable<T>
,他们将能够接受List<T>
,但反之则不然。请注意,这种替换并不总是可能的。如果在解决方案中的任何位置对返回的对象使用派生类型的方法,则 ReSharper 不会发出此建议。
我的观点是,IEnumerable<T>
类型带有很强的延迟执行语义,这可能会激励调用者将可枚举序列具体化为T[]
或List<T>
,以防他们必须多次枚举序列。具体化已经由底层数组或列表支持的序列是浪费,因此我强烈反对 ReSharper 的这一具体建议。我更喜欢返回通常与延迟执行无关的接口,例如IReadOnlyCollection<T>
、ICollection<T>
、IReadOnlyList<T>
或IList<T>
。
相关Microsoft规则:CA1851:IEnumerable
集合的可能多个枚举。