当我编写DAL或其他返回一组项的代码时,我是否应该始终使用RETURN语句:
1
| public IEnumerable<FooBar> GetRecentItems() |
或
1
| public IList<FooBar> GetRecentItems() |
目前,在我的代码中,我一直试图尽可能多地使用IEnumerable,但我不确定这是否是最佳实践?它看起来是正确的,因为我返回的是最通用的数据类型,同时仍然描述它的作用,但这可能不正确。
- 您询问的是列表还是列表?标题和问题说明了不同的事情…
- 如果可能,用户界面IEnumerable或IList instaead属于concrete类型。
- 我以list的形式返回集合。我不认为需要返回IEnumerable,因为您可以从列表中提取它。
- 另请参阅:stackoverflow.com/questions/381208/ienumerablet as return ty&zwnj;&8203;pe
- 可能重复的IEnumerableT作为返回类型
框架设计指南建议在需要返回可由调用方修改的集合或只读集合的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,使其不考虑任何需要的转换,从而优化编译的代码。
- @jon fdg建议使用collection或readOnlycollection作为集合类型的返回值。请参阅我的答案。
- 当你说"它会拯救CLR"时,你不清楚你最后一句话的意思。使用IEnumerable和IList可以节省多少?你能说得更清楚些吗?
- @在这个答案三年后,我认为你是对的——最后一部分是模糊的。如果期望IList作为参数的方法得到IEnumerable则必须手动将IEnumerable包装在新的列表或其他IList实现器中,而clr不会为您完成这项工作。相反——一个期望IEnumerable得到IList的方法可能需要进行一些取消绑定,但事后看来可能不需要这样做,因为IList实现了IEnumerable。
一般来说,您应该需要最通用的,并返回您可以返回的最具体的东西。所以,如果您有一个方法接受一个参数,并且您只需要IEnumerable中的可用内容,那么这应该是您的参数类型。如果方法可以返回IList或IEnumerable,则最好返回IList。这确保了它可以被最广泛的消费者使用。
在你需要的方面要宽松,在你提供的方面要明确。
- 我得出了相反的结论:应该接受特定类型,并返回一般类型。然而,您可能是对的,更广泛的应用方法比更受约束的方法更好。我得再想一想。
- 接受常规类型作为输入的理由是,它允许您使用尽可能广泛的输入范围,以便尽可能多地重用组件。另一方面,因为你已经知道了你手头上有什么样的东西,所以掩盖它没有多大意义。
- 我认为我同意有更多的通用参数,但是你返回一些不那么通用的参数的理由是什么?
- 好吧,让我换个方式试试。你为什么要扔掉信息?如果您只关心IEnumerable的结果,那么知道它是一个IList会不会伤害您?不,不是。在某些情况下,这可能是多余的信息,但对你没有伤害。现在为了利益。如果您返回一个列表或IList,我可以立即告诉您集合已经被检索,这是我无法用IEnumerable知道的。这可能是有用的信息,也可能不是有用的信息,但又一次,你为什么要扔掉这些信息?如果你知道一些额外的信息,把它传下去。
- 回想起来,我很惊讶没有人打电话给我。已经检索到IEnumerable。然而,IQueryable可以以非枚举状态返回。因此,一个更好的例子可能是返回iqueryable和ienumerable。返回更具体的IQueryable将允许进一步组合,而返回IEnumerable将强制立即枚举,并降低方法的可组合性。
- 我不确定这是正确的。许多LINQ方法执行延迟评估并返回IEnumerable。无法保证已枚举数据。
- 我认为问题是,如果在IEnumerable上添加更多的条件,那么在应用附加条件之前,它可能会在该点上进行评估,即使它尚未枚举。我认为这可能是特定于实现的,但我还没有确定到底是什么原因。我认为最安全的事情是假设一个iqueryable是一个"公平的游戏",为了进一步完善而不受惩罚,但同样不能说iEnumerable。你可以修改它,但是你不能保证"没有惩罚"部分。
- 我要继续打败这匹马。我意识到我可以更清楚。IEnumerable不一定"已经"被评估。iQuery和IEnumerable都支持延迟执行。主要的区别在于,如果您的IQueryable来自ORM,那么附加到IQueryable上的条件仍然可以找到进入SQL查询的方法。IEnumerable可能尚未进行计算,但添加到其上的其他谓词在查询中找不到它们的位置,将在初始检索后的第二轮中应用。那里…我想现在更清楚了。
这取决于…
返回派生最少的类型(IEnumerable将给您最大的余地来改变底层实现。
返回更派生的类型(IList为您的API用户提供了对结果的更多操作。
我总是建议返回具有您的用户将需要的所有操作的最小派生类型…因此,基本上,您首先必须在定义的API上下文中取消对结果的操作。
- 很好的通用答案,因为它也适用于其他方法。
- 我知道这个答案很古老…但它似乎与文档相矛盾:"如果idictionaryInterface或IListInterface满足所需集合的要求,从ICollectionInterface派生新的集合类以获得更大的灵活性",这似乎意味着应该首选派生的类型。(msdn.microsoft.com/en-us/library/92t2ye13(v=vs.110).aspx)
需要考虑的一点是,如果您使用延迟执行的LINQ语句来生成您的IEnumerable,那么在从方法返回之前调用.ToList(),这意味着您的项可能会被重复两次-一次创建列表,另一次当调用者循环、筛选或转换您的返回值时。在实际操作中,我喜欢避免将LINQ的结果转换为对象,直到不得不转换为具体的列表或字典为止。如果我的调用者需要一个列表,这是一个简单的方法调用-我不需要为他们做决定,这使得我的代码在调用者只是做foreach的情况下稍微更有效率。
- @乔尔·穆勒,我通常会对它们调用tolist()。我一般不喜欢把iquerieble暴露在我的其他项目中。
- 我指的更多的是linq-to-objects,iqueryable通常不输入图片。当涉及到数据库时,tolist()变得更为必要,因为否则在迭代之前可能会有关闭连接的风险,而这并不能很好地工作。但是,如果这不是问题,那么很容易将iqueryable公开为IEnumerable,而不必在希望隐藏iqueryable时强制执行额外的迭代。
List提供了调用代码更多的特性,如修改返回的对象和按索引访问。所以问题可以归结为:在应用程序的特定用例中,您想支持这样的使用吗(大概是通过返回一个新构建的集合!),为了方便调用者——或者,当调用者需要循环访问集合时,您希望在简单情况下加快速度,并且您可以安全地返回对真正的基础集合的引用,而不必担心这会导致错误的更改,等等?
只有您能够回答这个问题,并且只有充分了解您的调用者希望如何处理返回值,以及这里的性能有多重要(您要复制的集合有多大,这有多可能成为瓶颈等)。
- "安全地返回对真实基础集合的引用,而不担心它会被错误地更改"-即使您返回IEnumerable,他们也不能简单地将其转换回List并更改它吗?
- 并非每个IEnumable都是一个列表。如果返回的对象不是继承自list或实现ilist的类型,这将导致InvalidcastException。
- list有一个问题,它会将您锁定到特定的实现集合中或readOnlyCollection是首选的
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,因为它只提供迭代和基本功能,这在设计上是我喜欢的。我的选票总是反对,但主要是你喜欢什么,不喜欢什么。在性能方面,我认为它们更为相同。
我想你也可以用,但每个都有用处。基本上,List是IEnumerable,但您有计数功能、添加元素、删除元素。
IEnumerable对于计算元素或获取集合中的特定元素无效。
List是一个集合,它非常适合查找特定的元素,易于添加或删除元素。
一般情况下,我尽量使用List,因为这给了我更多的灵活性。
使用List getRecentItems()而不是6