【问题标题】:Linq Should I Return List<T> Or IEnumerable<T> When I Still May Do More LaterLinq 我应该返回 List<T> 还是 IEnumerable<T> 当我以后还可以做更多的时候
【发布时间】:2011-04-26 13:38:20
【问题描述】:

我有一些方法可以返回一个 T 列表,比如 GetAllEvents。在某些情况下,我需要按日期或项目上的某些其他属性过滤该事件列表(或我的列表是什么)。

我知道 LINQ 查询可以“链接”或有 x 行进一步细化它们,并且查询将不会执行,直到您需要在非 linq 语句中实际使用它们时(请纠正我,如果我对这个信念是错误的。)

我的问题是,如果我的 GetAllXXX 方法返回我得到的任何内容的列表,我在执行 LINQ 的 GetAllXXX 代码末尾使用的 .ToList() 方法是什么?我应该返回 IEnumerable 吗?如果仅适用于在实际运行查询之前我需要对“结果”做进一步处理的情况。

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

有什么指点或建议吗?

【问题讨论】:

  • 不要返回List&lt;T&gt;。但是你可以考虑返回IList&lt;T&gt;

标签: c# linq memory-management


【解决方案1】:

返回IEnumerable

List 的转换既快速又轻松,而且您可以将接口与方法的实现和输出的使用解耦。

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

【讨论】:

  • 我完全同意。不过,值得考虑的一个小警告是:如果您最终没有将 IEnumerable 转换为数组或列表,并且对该 IEnumerable 进行了多次操作,您可能会发现自己无意中多次执行查询,这可能会造成严重的性能问题。有时将返回类型设为 IEnumerable 也不错,但在返回对象之前仍将其转换为数组。
【解决方案2】:

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

如果您只是返回序列,那么您就是通过创建一个不同的问题来“解决”您的问题。现在,当客户端过滤列表时,他们必须对服务器进行一千次小点击,而不是一次昂贵的点击。就像通过网络传输的信息一样多,而且每次点击开销都需要更长的时间。

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

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

【讨论】:

    【解决方案3】:

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

    interface IOneTripEnumerable<T> : IEnumerable<T>
    

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

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

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

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

    【讨论】:

    • 这是一个好点;事实上,大多数 LINQ 提供程序并没有实现所有可能的 IQueryable 功能的全部荣耀。
    猜你喜欢
    • 2011-03-03
    • 1970-01-01
    • 2021-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-24
    相关资源
    最近更新 更多