今天我注意到当我在大数据上运行几个LINQ语句时,所花费的时间可能会有很大差异。
假设我们有这样的查询:
var conflicts = features.Where(/* some condition */);
foreach (var c in conflicts) // log the conflicts
其中features
是表示表中行的对象列表。因此,这些对象非常复杂,甚至查询它们的一个简单属性是一个巨大的操作(包括实际的数据库查询,验证,状态更改......)我认为执行这样的查询需要很长时间。错误:第一个语句在很短的时间内执行,而简单地循环结果则是永恒的。
但是,如果我使用IEnumerable#ToList()
将LINQ表达式检索的集合转换为List,则第一个语句运行稍慢并且循环结果非常快。话虽如此,第二次接近的完整持续时间远小于不转换为列表时的持续时间。
var conflicts = features.Where(/* some condition */).ToList();
foreach (var c in conflicts) // log the conflicts
所以我认为var conflicts = features.Where
实际上并不查询,而是准备数据。但我不明白为什么转换到列表然后循环比这快得多。 这是实际问题
有人对此有解释吗?
答案 0 :(得分:5)
本声明,只是声明你的意图:
var conflicts = features.Where(...);
获取满足Where
子句中条件的数据。然后当你写这个
foreach (var c in conflicts)
将执行实际查询并开始获取结果。这称为lazy loading
。我们用于此的另一个术语是默认执行。我们不断执行查询,直到我们需要它的数据。
另一方面,如果你做过这样的事情:
var conflicts = features.Where(...).ToList();
将创建一个内存集合,其中将存储查询结果。在这种情况下,查询将立即执行。
一般来说,正如您在维基百科中读到的那样:
延迟加载是计算机编程中常用的设计模式 将对象的初始化推迟到它所在的点 需要。如果,它可以提高程序操作的效率 适当和适当使用。与延迟加载相反的是渴望 负荷。
<强>更新强>
我认为这种内存中的集合比起来时要快得多 懒加载?
Here是一篇很棒的文章,可以回答您的问题。
答案 1 :(得分:0)
欢迎来到懒惰评价的精彩世界。使用LINQ,在需要结果之前不会执行查询。在尝试获取结果之前(ToList()获取结果并将其放入列表中)您只是创建查询。将其视为编写代码与运行程序。虽然这可能令人困惑,并且可能导致代码在意外时间执行,甚至多次执行,例如,如果您将查询提前两次,这实际上是一件好事。它允许您拥有一段返回查询的代码(不是结果而是实际查询),并让另一段代码根据原始查询创建一个新查询。例如,您可以在原件上添加其他过滤器或将其分页。
您看到的性能差异基本上是代码中不同位置发生的数据库调用。