【问题标题】:Do you ToList()?你 ToList() 吗?
【发布时间】:2008-12-02 16:26:02
【问题描述】:

您是否有喜欢在处理 LINQ 查询结果时使用的默认类型?

默认情况下,LINQ 将返回 IEnumerable<>IOrderedEnumerable<>。我们发现List<> 通常对我们更有用,因此我们养成了ToList() 查询的习惯,并且在我们的函数参数和返回值中肯定使用List<>

唯一的例外是在 LINQ to SQL 中调用 .ToList() 会过早地枚举 IEnumerable

我们也在广泛使用 WCF,默认的集合类型是System.Array。我们总是在 VS2008 的“服务参考设置”对话框中将其更改为 System.Collections.Generic.List,以与我们代码库的其余部分保持一致。

你是做什么的?

【问题讨论】:

    标签: linq linq-to-sql


    【解决方案1】:

    ToList always 立即评估序列 - 而不仅仅是在 LINQ to SQL 中。如果你想要,那很好 - 但它并不总是合适的。

    我个人会尽量避免声明您直接返回List<T> - 通常IList<T> 更合适,并允许您稍后更改为不同的实现。当然,有些操作只能在List<T> 本身上指定……这种决定总是很棘手。

    编辑:(我会将此放在评论中,但它会太庞大。)延迟执行允许您处理太大而无法放入内存的数据源。例如,如果您正在处理日志文件 - 将它们从一种格式转换为另一种格式,将它们上传到数据库中,计算一些统计数据或类似的东西 - 您很可能能够通过流式处理任意数量的数据,但你真的想把所有的东西都吸进内存。对于您的特定应用程序而言,这可能不是问题,但需要牢记。

    【讨论】:

    • 同意 ToList 立即计算。我们的想法是,在 LINQtoSQL 中,这可能会对性能产生影响(特别是如果我们将几个 LINQ 表达式链接在一起),但是当我们在内存中时,任何性能影响都可以忽略不计 - 人类的一致性更重要很重要。
    • 除了性能之外还有显着差异。特别是,如果查询(例如源中的数据)发生任何变化,那么延迟执行会给你一个不同的答案。有时这就是你想要的,有时不是。
    【解决方案2】:

    我们有相同的场景 - WCF 与服务器通信,服务器使用 LINQtoSQL。

    我们在从服务器请求对象时使用 .ToArray() ,因为客户端更改列表是“非法的”。 (也就是说,没有目的支持“.Add”、“.Remove”等)。

    不过,虽然仍在服务器上,但我建议您将其保留为默认值(不是 IEnumerable,而是 IQueryable)。这样,如果您想根据某些条件进行更多过滤,则在 SQL 端进行过滤STILL,直到被评估。

    这是非常重要的一点,因为这意味着取决于您所做的事情,您会获得难以置信的性能收益或损失。

    示例:

    // This is just an example... imagine this is on the server only. It's the
    // basic method that gets the list of clients.
    private IEnumerable<Client> GetClients()
    {
        var result = MyDataContext.Clients;  
    
        return result.AsEnumerable();
    }
    
    // This method here is actually called by the user...
    public Client[] GetClientsForLoggedInUser()
    {
        var clients = GetClients().Where(client=> client.Owner == currentUser);
    
        return clients.ToArray();
    }
    

    你看到那里发生了什么吗? “GetClients”方法将强制从数据库中下载所有“客户端”...然后在 GetClientsForLoogedInUser 方法中将发生 Where 子句以将其过滤掉。

    现在,请注意细微的变化:

    private IQueryable<Client> GetClients()
    {
        var result = MyDataContext.Clients;  
    
        return result.AsQueryable();
    }
    

    现在,在调用“.ToArray”之前不会进行实际评估...并且 SQL 将进行过滤。好多了!

    【讨论】:

    • 您的观点非常明确。大多数人似乎都错过了它。我经常看到像这样的非常糟糕的例子。人们不会停下来思考演员在执行时会做什么。
    【解决方案3】:

    在 Linq-to-Objects 的情况下,从函数返回 List&lt;T&gt; 不如返回 IList&lt;T&gt; 好,正如 THE VENERABLE SKEET 指出的那样。但通常你仍然可以做得更好。如果您要返回的东西应该是不可变的,那么 IList 是一个糟糕的选择,因为它会邀请调用者添加或删除东西。

    例如,有时你有一个返回 Linq 查询结果的方法或属性,或者使用 yield return 懒惰地生成一个列表,然后你意识到第一次这样做会更好调用,将结果缓存在List&lt;T&gt; 中,然后返回缓存的版本。那时返回 IList 可能不是一个好主意,因为调用者可能会出于自己的目的修改列表,这会破坏您的缓存,使所有其他调用者都可以看到他们的更改。

    最好返回IEnumerable&lt;T&gt;,所以他们只有前向迭代。如果调用者想要快速随机访问,即他们希望他们可以使用 [] 通过索引访问,他们可以使用 ElementAt,这是 Linq 定义的,以便它安静地嗅探IList 并在可用时使用它,如果不是它执行愚蠢的线性查找。

    我使用ToList 的一件事是当我有一个复杂的Linq 表达式系统与使用yield return 过滤或转换列表的自定义运算符混合时。在调试器中单步执行可能会非常混乱,因为它会跳来跳去进行延迟评估,所以我有时会临时将 ToList() 添加到几个地方,以便我可以更轻松地跟踪执行路径。 (虽然如果你正在执行的事情有副作用,这可能会改变程序的含义。)

    【讨论】:

    【解决方案4】:

    这取决于您是否需要修改集合。当我知道没有人会添加/删除项目时,我喜欢使用数组。当我需要排序/添加/删除项目时,我会使用列表。但是,通常我会尽可能将其保留为 IEnumerable。

    【讨论】:

      【解决方案5】:

      如果您不需要 List 的附加功能,为什么不坚持使用 IQueryable ?!?!?!最小公分母是最好的解决方案(尤其是当您看到 Timothy 的答案时)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-07-28
        • 1970-01-01
        • 2011-02-15
        相关资源
        最近更新 更多