Linq我应该返回列表<t>或IEnumerable <t>我还可以做更多事情</t> </t>

时间:2011-04-26 13:38:21

标签: c# linq memory-management

我有一些返回List of T的方法,比如说GetAllEvents。在某些情况下,我需要按日期或项目上的其他属性过滤事件列表(或任何我的列表)。

我知道LINQ查询可以被“链接”或者有x行可以进一步细化它们,并且在你需要在非linq语句中实际使用它们之前,查询将不会执行(请纠正我,如果我对这个信徒不对。)

我的问题是,如果我的GetAllXXX方法返回我得到的任何List,那么我在执行LINQ的GetAllXXX代码结束时使用的.ToList()方法是什么?我应该返回IEnumerable吗?如果只是那些我需要在实际运行查询之前对“结果”做进一步操作的情况。

这是我担心的一个例子:我说过1000个事件。 GetAllEvents将检索所有1000并给我一个列表。然后,根据用户所在的页面,可能仅显示今天,本周或某个类别的事件。理想情况下,当它到达我向用户显示今天发生的5个事件的时候,我真的不想通过线路传递所有1000,然后将其截断到他们真正想要的5。是的,我知道在这一点上它都是服务器端,但是如果它仍在为1000分配内存我试图避免这种情况。

有任何指示或建议吗?

3 个答案:

答案 0 :(得分:12)

返回IEnumerable

转换为List既快速又轻松,另外,您可以将界面与方法的实现和输出的使用分开。

关于您的特定担心 - 如果返回所有1000个事件并在客户端上处理它们会很昂贵,那么您应该考虑在服务器上进行一些过滤。您仍然可以使用返回所有事件的方法,但具有返回最频繁查询的专用/优化版本。今天的事件将是一个很好的例子。

答案 1 :(得分:9)

如果您将序列转换为服务器上的列表,那么您在服务器上使用时间和内存,然后通过线路传输整个内容,然后在客户端上使用更多时间和内存来过滤列表。

如果您只是返回序列,那么您将通过创建不同的问题来“解决”您的问题。现在,当客户端过滤列表时,他们必须对服务器进行一千次小命中,而不是一次点击。同样多的信息正在通过网络传输,并且在所有每次命中的开销上花费的时间更长。

如果你想要做的是在服务器上执行过滤,那么你可以(1)创建一个代表常见过滤器的自定义API(简单),或者(2)代替返回IQueryable,并实现LINQ提供程序。 (很难但很强大。)IQueryable允许你在客户端构建查询,通过网络将查询发送到服务器,在服务器上运行查询,然后只提供客户端想要的结果。

我的同事马特沃伦写了一系列关于如何实现IQueryable的文章;我会从那开始。

答案 2 :(得分:8)

Eric Lippert的答案很好,但请注意,如果您只想提供一些服务器端过滤(甚至是自定义过滤),则不需要实现整个IQueryable。您可以通过仅实现您实际使用的LINQ函数来创建更简单的LINQ兼容API。例如,考虑定义

interface IOneTripEnumerable<T> : IEnumerable<T>

它只公开一个LINQ兼容的Where方法,返回类型为IOneTripEnumerable

IOneTripEnumerable.Where的实现将返回一个也实现IOneTripEnumerable的新对象,并将过滤器存储为数据成员。调用IOneTripEnumerable.GetEnumerator时,您可以打包过滤器并将它们发送到服务器,然后在一次往返中返回过滤后的结果。

(您还可以实现客户端缓存策略:如果您希望后续调用GetEnumerator以与初始调用相同的结果返回枚举数,只需将结果存储在可枚举对象中。)

如果您有更多时间并且看到需要,可以通过添加其他LINQ方法进一步优化,但只需控制Where(允许在服务器上进行过滤)和GetEnumerator(以获取所有结果)单一往返)可以以低成本实现相当好的结果。您不需要实现整个IQueryable。 (请注意,Count,Any和Take也非常适合往返优化,并且实现起来很简单。)