我主要理解延迟执行,但我对特定情况有疑问:
给定一个代码片段,如
var resultsOfInterest = from r in ...
select r;
foreach (var x in resultsOfInterest)
{
//do something with x
}
Run Code Online (Sandbox Code Playgroud)
查询resultsOfInterest 执行了多少次?一旦设置foreach循环,或每个元素'x'一次?是否会更有效率
foreach (var x in resultsOfInterest.ToArray())
{
//do something with x
}
Run Code Online (Sandbox Code Playgroud)
?
TIA
它将在循环之前执行一次,当GetEnumerator()方法将在查询变量上执行时.以下是foreach循环的样子:
var enumerator = resultsOfInterest.GetEnumerator(); // query executed here
while(enumerator.MoveNext()) // iterating over results of query execution
{
var x = enumerator.Current;
// do something with x
}
Run Code Online (Sandbox Code Playgroud)
第二个样本效率不高,它只是将查询执行结果存储在数组中,然后调用数组迭代器:
var enumerator = resultsOfInterest.ToArray().GetEnumerator();
// loop stays same
Run Code Online (Sandbox Code Playgroud)
在这两种情况下,它只运行一次。
在第一个示例中(如果这是 Linq-to-Objects 查询),它运行的时间足以x在每次迭代中获取下一个查询。在第二个示例中,它必须立即计算整个结果集并将其存储到数组中。
因此,假设这是一个昂贵的查询,获取每个项目需要 1 秒,并且列表中有 20 个项目,两个查询将花费大约 20 秒来处理所有项目。但是,第一个在每次迭代中获取下一个项目时将被阻塞 1 秒,但第二个将在循环开始之前被阻塞 20 秒,然后相当快地循环遍历数组中的所有项目。
在实际评估查询时,两者都没有更有效。但是,一般来说,您应该避免不必要的调用ToArrayor ToList,因为除了评估查询之外,它还必须为结果分配一个数组(List<T>将其项目存储在内部数组中)。对于包含 20 个项目的列表,这意义不大,但当您有数千个项目时,这可能会导致速度明显减慢。当然,这并不意味着这ToArray总是不好的。如果在前面的示例中有 5 个foreach循环,则将结果存储在数组中并循环遍历该数组,而不是每次都重新评估查询,实际上会使代码速度加快约 80 秒