我应该总是返回IEnumerable <t>而不是IList <t>?</t> </t>

时间:2009-07-02 05:15:36

标签: c# ienumerable

当我编写DAL或其他返回一组项目的代码时,我是否应该总是返回声明:

public IEnumerable<FooBar> GetRecentItems()

public IList<FooBar> GetRecentItems()

目前,在我的代码中,我一直在尽量使用IEnumerable,但我不确定这是否是最佳做法?这似乎是正确的,因为我正在返回最通用的数据类型,同时仍然描述它的作用,但也许这是不正确的。

14 个答案:

答案 0 :(得分:61)

当您需要返回可由调用者修改的集合或Collection的只读集合时,

框架设计指南建议使用类ReadOnlyCollection

这是一个简单的IList首选的原因是IList不通知调用者它是否只读。

如果您返回IEnumerable<T>,则某些操作可能对调用者来说有点棘手。此外,您不再为调用者提供修改集合的灵活性,这可能是您可能想要或不想要的。

请记住,LINQ包含一些技巧,并会根据执行的类型优化某些调用。因此,例如,如果您执行Count并且底层集合是List,则它不会遍历所有元素。

就个人而言,对于ORM,我可能会坚持使用Collection<T>作为我的返回值。

答案 1 :(得分:41)

这实际上取决于您使用该特定界面的原因。

例如,IList<T>有几种IEnumerable<T>中没有的方法:

  • IndexOf(T item)
  • Insert(int index, T item)
  • RemoveAt(int index)

和属性:

  • T this[int index] { get; set; }

如果您以任何方式需要这些方法,那么请务必返回IList<T>

此外,如果使用IEnumerable<T>结果的方法期望IList<T>,它将保留CLR不考虑所需的任何转换,从而优化编译的代码。

答案 2 :(得分:19)

这取决于......

返回最少派生类型(IEnumerable)将为您提供最大的余地来改变基础实现。

返回更多派生类型(IList)可为您的API用户提供更多结果操作。

我总是建议返回最少派生类型,其中包含您的用户将需要的所有操作...所以基本上,您首先必须在您定义的API的上下文中去除对结果的哪些操作有意义

答案 3 :(得分:19)

一般情况下,您应该要求最通用,并返回最具体的内容。因此,如果你有一个带参数的方法,并且你只需要IEnumerable中可用的那个,那么那应该是你的参数类型。如果您的方法可以返回IList或IEnumerable,则更喜欢返回IList。这确保了最广泛的消费者可以使用它。

在你需要的东西上松散,并明确你所提供的东西。

答案 4 :(得分:9)

要考虑的一件事是,如果您使用延迟执行LINQ语句来生成IEnumerable<T>,则在从方法返回之前调用.ToList()意味着您的项目可能会被迭代两次 - 一次创建列表,一旦调用者循环,过滤或转换您的返回值。在实际应用中,我希望避免将LINQ-to-Objects的结果转换为具体的List或Dictionary,直到我不得不这样做。如果我的调用者需要一个List,那就是一个简单的方法调用 - 我不需要为他们做出决定,这使得我的代码在调用者正在做foreach的情况下稍微更有效。

答案 5 :(得分:7)

List<T>为调用代码提供了更多功能,例如修改返回的对象和按索引访问。因此,问题可归结为:在您的应用程序的特定用例中,您是否希望支持此类用途(可能是通过返回一个新构建的集合!),以便调用者方便 - 或者您是否希望速度为简单的情况下所有调用者需要循环遍历集合,你可以安全地返回对真实底层集合的引用,而不用担心会错误地改变它等等吗?

只有你能回答这个问题,并且只能理解你的来电者想要对返回值做什么,以及这里的表现有多重要(你要复制的收藏有多大,这有多大可能性)瓶颈等等。

答案 6 :(得分:4)

  

我认为你可以使用其中任何一个,但每个都有用。基本上ListIEnumerable,但你有Parent   计数功能,添加元素,删除元素

     

IEnumerable对于计算元素

效率不高

如果集合只是为了只读,或者集合的修改由IList控制,那么只为Count返回Count()不是一个好主意。

在Linq中,IEnumerable<T>上有一个.Count扩展方法,如果基础类型为IList,则CLR内部将快捷方式manufacturer.Models.Add(model),因此性能差异为可以忽略不计。

一般来说,我觉得(意见)最好在可能的情况下返回IEnumerable,如果你需要做添加,然后将这些方法添加到父类,否则消费者会在Model中管理违反原则的集合,例如: public interface IManufacturer { IEnumerable<Model> Models {get;} void AddModel(Model model); } 违反了德米特定律。当然,这些只是指导方针,而不是硬性规定,但在您完全掌握适用性之前,盲目追求比完全不遵循要好。

{{1}}

(注意:如果使用nNHibernate,您可能需要使用不同的访问器映射到私有IList。)

答案 7 :(得分:1)

当你谈论返回值而不是输入参数时,这并不是那么简单。当它是输入参数时,您确切地知道您需要做什么。因此,如果您需要能够遍历集合,则需要使用IEnumberable,而如果需要添加或删除,则需要使用IList。

在返回值的情况下,它更难。你的来电者期待什么?如果你返回一个IEnumerable,那么他就不会知道他可以从中做出一个IList。但是,如果你返回一个IList,他就会知道他可以迭代它。因此,您必须考虑调用者将对数据执行的操作。调用者需要/期望的功能是在决定返回什么时应该管理的内容。

答案 8 :(得分:0)

我认为你可以使用其中任何一个,但每个都有用。基本上ListIEnumerable,但您有计数功能,添加元素,删除元素

IEnumerable对于计算元素或获取集合中的特定元素效率不高。

List是一个非常适合查找特定元素,易于添加元素或删除元素的集合。

一般情况下,我尝试尽可能使用List,因为这会给我更大的灵活性。

使用     List<FooBar> getRecentItems() 而不是     IList<FooBar> GetRecentItems()

答案 9 :(得分:0)

所有人都说它取决于, 如果你不想在调用层添加/删除功能,那么我将投票给IEnumerable,因为它只提供迭代和基本功能,这在设计预期我喜欢。 返回IList我的投票总是重新获得,但它主要是你喜欢和不喜欢的。 在性能方面,我认为它们更相同。

答案 10 :(得分:0)

如果不计算外部代码,最好还是返回IEnumerable,因为稍后您可以更改实现(不受外部代码影响),例如, yield iterator 逻辑和保存内存资源(顺便提一下非常好的语言功能)。

但是,如果您需要项目计数,请不要忘记IEnumerable和IList之间还有另一层 - ICollection

答案 11 :(得分:0)

我可能有点偏离这里,看到目前为止没有其他人建议,但为什么不返回(I)Collection<T>

根据我的记忆,Collection<T>List<T>的首选返回类型,因为它抽象了实现。他们都实现IEnumerable,但这对我来说听起来有点太低了。

答案 12 :(得分:0)

我认为一般规则是使用更具体的类来返回,以避免做不需要的工作并为调用者提供更多选项。

那就是说,我认为考虑你正在编写的代码比下一个人编写的代码(在合理范围内)更重要。这是因为你可以对已经存在的代码做出假设。

请记住,从界面中的IEnumerable向上移动到集合将起作用,从集合向下移动到IEnumerable会破坏现有代码。

如果这些意见似乎都有冲突,那是因为这个决定是主观的。

答案 13 :(得分:0)

TL; DR; –摘要

  • 如果您开发内部软件,请务必使用特定类型(如List)作为回报 值和输入参数的最通用类型,即使是集合也是如此。
  • 如果方法是可再发行库的公共API的一部分,请使用 接口而不是具体的集合类型来引入返回值和输入参数。
  • 如果方法返回只读集合,请使用IReadOnlyListIReadOnlyCollection作为返回值类型。

More