我应该总是返回IEnumerable <T>而不是IList <T>吗?

Kin*_*tor 94 c# ienumerable

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

public IEnumerable<FooBar> GetRecentItems()
Run Code Online (Sandbox Code Playgroud)

要么

public IList<FooBar> GetRecentItems()
Run Code Online (Sandbox Code Playgroud)

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

Sam*_*ron 65

当您需要返回可由调用者或ReadOnlyCollection为只读集合修改的集合时,框架设计指南建议使用类Collection.

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

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

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

就个人而言,对于ORM,我可能会坚持IList作为我的返回值.

  • [收集指南](https://msdn.microsoft.com/en-us/library/dn169389.aspx)包含更详细的DO和DONT列表. (10认同)

Jon*_*jap 44

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

例如,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不考虑所需的任何转换,从而优化编译的代码.

  • @Jon FDG建议使用Collection <T>或ReadOnlyCollection <T>作为集合类型的返回值,请参阅我的答案. (2认同)
  • @CoffeeAddict 在这个答案三年后,我认为你是对的——最后一部分是模糊的。如果需要 IList&lt;T&gt; 作为参数的方法获取 IEnumerable&lt;T&gt;,则必须将 IEnumerable 手动包装在新的 List&lt;T&gt; 或其他 IList&lt;T&gt; 实现器中,并且该工作不会由适合您的 CLR。相反 - 期望 IEnumerable&lt;T&gt; 获取 IList&lt;T&gt; 的方法,*可能*必须执行一些拆箱操作,但事后看来可能不需要,因为 IList&lt;T&gt; 实现了 IEnumerable&lt;T&gt;。 (2认同)

Mel*_*Mel 22

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

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

  • 好的,让我试试另一种方式.你为什么要扔掉信息?如果您只关心IEnumerable <T>的结果,那么它是否会以任何方式让您知道它是IList <T>?不,它没有.在某些情况下,它可能是多余的信息,但它不会对您造成伤害.现在为了好处.如果你返回一个List或IList,我可以立即告诉你已经检索到了这个集合,这是我用IEnumerable无法知道的.这可能是也可能不是有用的信息,但再一次,你为什么要扔掉信息呢?如果您知道有关某事的额外信息,请将其传递给您. (5认同)
  • 接受通用类型作为输入的理由是,它允许您使用尽可能广泛的输入,以便从组件中获得尽可能多的重用。另一方面,由于您已经确切知道您可以使用的对象,因此遮罩它没有多大意义。 (2认同)
  • 我不确定这是否正确。许多 LINQ 方法执行惰性求值并返回“IEnumerable”。不保证数据已被枚举。 (2认同)

jer*_*jvl 20

那要看...

返回最少派生类型(IEnumerable)将使您有最大的余地来改变轨道上的底层实现.

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

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


Joe*_*ler 11

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

  • 我更多地提到LINQ-to-Objects,其中IQueryable通常不会进入图片.当涉及数据库时,ToList()变得更加必要,因为否则您可能会在迭代之前关闭连接,这不能很好地工作.但是,如果这不是问题,那么当您想要隐藏IQueryable时,很容易将IQueryable公开为IEnumerable而不强制进行额外的迭代. (3认同)

Ale*_*lli 8

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

只有你能回答这个问题,并且只能理解你的呼叫者想要对返回值做什么,以及这里的性能有多重要(你要复制的集合有多大,这有多大可能成为瓶颈,等等).

  • List &lt;T&gt;具有将您锁定到特定实现的问题,首选Collection &lt;T&gt;或ReadOnlyCollection &lt;T&gt; (2认同)

小智 5

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

IEnumerable 对元素计数效率不高

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

在 Linq 中,有一个Count()扩展方法,如果基础类型为,则IEnumerable<T>CLR 内部将快捷地调用该扩展方法,因此性能差异可以忽略不计。.CountIList

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

public interface IManufacturer 
{
     IEnumerable<Model> Models {get;}
     void AddModel(Model model);
}
Run Code Online (Sandbox Code Playgroud)

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