关于c#:我应该总是返回IEnumerable< T>

Should I always return IEnumerable<T> instead of IList<T>?

当我编写DAL或其他返回一组项的代码时,我是否应该始终使用RETURN语句:

1
public IEnumerable<FooBar> GetRecentItems()

1
public IList<FooBar> GetRecentItems()

目前,在我的代码中,我一直试图尽可能多地使用IEnumerable,但我不确定这是否是最佳实践?它看起来是正确的,因为我返回的是最通用的数据类型,同时仍然描述它的作用,但这可能不正确。


框架设计指南建议在需要返回可由调用方修改的集合或只读集合的readOnlyCollection时使用类集合。

与简单的IList相比,这是首选的原因,因为IList不会通知调用方它是否为只读。

如果您返回一个IEnumerable,那么某些操作对于调用者来说可能更难执行。此外,您不再为调用者提供修改集合的灵活性,您可能需要也可能不需要。

记住,Linq包含一些技巧,并将根据执行的类型优化某些调用。因此,例如,如果您执行一个计数,并且基础集合是一个列表,那么它不会遍历所有元素。

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


这实际上取决于您为什么要使用那个特定的接口。

例如,IList有几种方法在IEnumerable中没有出现:

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

性质:

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

如果您以任何方式需要这些方法,那么无论如何返回IList

also,如果使用IEnumerable结果的方法需要一个IList,它将保存clr,使其不考虑任何需要的转换,从而优化编译的代码。


一般来说,您应该需要最通用的,并返回您可以返回的最具体的东西。所以,如果您有一个方法接受一个参数,并且您只需要IEnumerable中的可用内容,那么这应该是您的参数类型。如果方法可以返回IList或IEnumerable,则最好返回IList。这确保了它可以被最广泛的消费者使用。

在你需要的方面要宽松,在你提供的方面要明确。


这取决于…

返回派生最少的类型(IEnumerable将给您最大的余地来改变底层实现。

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

我总是建议返回具有您的用户将需要的所有操作的最小派生类型…因此,基本上,您首先必须在定义的API上下文中取消对结果的操作。


需要考虑的一点是,如果您使用延迟执行的LINQ语句来生成您的IEnumerable,那么在从方法返回之前调用.ToList(),这意味着您的项可能会被重复两次-一次创建列表,另一次当调用者循环、筛选或转换您的返回值时。在实际操作中,我喜欢避免将LINQ的结果转换为对象,直到不得不转换为具体的列表或字典为止。如果我的调用者需要一个列表,这是一个简单的方法调用-我不需要为他们做决定,这使得我的代码在调用者只是做foreach的情况下稍微更有效率。


List提供了调用代码更多的特性,如修改返回的对象和按索引访问。所以问题可以归结为:在应用程序的特定用例中,您想支持这样的使用吗(大概是通过返回一个新构建的集合!),为了方便调用者——或者,当调用者需要循环访问集合时,您希望在简单情况下加快速度,并且您可以安全地返回对真正的基础集合的引用,而不必担心这会导致错误的更改,等等?

只有您能够回答这个问题,并且只有充分了解您的调用者希望如何处理返回值,以及这里的性能有多重要(您要复制的集合有多大,这有多可能成为瓶颈等)。


I think you can use either, but each has a use. Basically List is IEnumerable but you have
count functionality, add element, remove element

IEnumerable is not efficient for counting elements

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

在linq中,IEnumerable上有一个Count()扩展方法,如果底层类型是IList的话,clr内部会捷径到.Count,所以性能差异可以忽略。

一般来说,我认为(意见)最好在可能的情况下返回IEnumerable,如果您需要进行添加,然后将这些方法添加到父类中,否则消费者将在模型中管理集合,这违反了原则,例如,manufacturer.Models.Add(model)违反了demeter法。当然,这些只是指导方针,并不是硬性的、快速的规则,但是在你完全掌握了适用性之前,盲目的遵循总比完全不遵循要好。

1
2
3
4
5
public interface IManufacturer
{
     IEnumerable<Model> Models {get;}
     void AddModel(Model model);
}

(注意:如果使用nnhibernate,可能需要使用不同的访问器映射到private ilist。)


当你谈论的是返回值而不是输入参数时,就不那么简单了。当它是一个输入参数时,您完全知道需要做什么。因此,如果您需要能够遍历集合,那么您需要一个IEnumerable,而如果您需要添加或删除,则需要一个IList。

在回报值的情况下,这是更困难的。打电话的人期望什么?如果你返回一个IEnumerable,那么他就不会知道他能从中得到一个IList。但是,如果您返回一个IList,他将知道他可以遍历它。因此,您必须考虑到您的调用者将如何处理这些数据。您的调用者需要/期望的功能是在决定返回什么时应该控制什么。


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

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

记住,从接口中的IEnumerable向上移动到集合是有效的,从集合向下移动到IEnumerable将破坏现有代码。

如果这些观点看起来都是矛盾的,那是因为这个决定是主观的。


我可能有点不在这里,因为到目前为止还没有其他人提出建议,但是你为什么不退回一辆(I)Collection呢?

据我所知,与List相比,Collection是首选的返回类型,因为它抽象了实现。他们都执行IEnumerable,但这对我来说有点太低了。


如果不计入外部代码的数量,则最好返回IEnumerable,因为稍后可以更改实现(不影响外部代码),例如,生成迭代器逻辑并节省内存资源(顺便说一下,这是非常好的语言功能)。

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


正如大家所说,这取决于,如果您不希望在调用层添加/删除功能性,那么我将投票给IEnumerable,因为它只提供迭代和基本功能,这在设计上是我喜欢的。我的选票总是反对,但主要是你喜欢什么,不喜欢什么。在性能方面,我认为它们更为相同。


我想你也可以用,但每个都有用处。基本上,ListIEnumerable,但您有计数功能、添加元素、删除元素。

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

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

一般情况下,我尽量使用List,因为这给了我更多的灵活性。

使用List getRecentItems()而不是6