我有一个项目列表和一个LINQ查询。现在,使用LINQ的延迟执行,后续的foreach循环是否只执行一次查询或循环中的每一轮?
给出这个例子(取自Introduction to LINQ Queries (C#), on MSDN)
// The Three Parts of a LINQ Query:
// 1. Data source.
int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };
// 2. Query creation.
// numQuery is an IEnumerable<int>
var numQuery =
from num in numbers
where (num % 2) == 0
select num;
// 3. Query execution.
foreach (int num in numQuery)
{
Console.Write("{0,1} ", num);
}
或者,换句话说,如果我有:
会有什么不同 foreach (int num in numQuery.ToList())
而且,如果底层数据不在数组中,而是在数据库中吗?
答案 0 :(得分:18)
现在,使用LINQ的延迟执行,后续的foreach循环是否只执行一次查询或循环中的每次转弯?
是的,一次循环。实际上,它可能不到一次执行查询 - 您可以中止循环部分,并且不会对任何剩余项执行(num % 2) == 0
测试。
或者,换句话说,如果我有,那会有什么不同:
foreach (int num in numQuery.ToList())
两个不同之处:
在上面的情况中,ToList()
浪费时间和内存,因为它首先与初始foreach
做同样的事情,从中构建一个列表,然后foreach
列表。根据结果的大小,差异将介于微不足道和防止代码工作之间。
但是,如果您要反复 对同一结果进行foreach
,或以其他方式重复使用,那么foreach
只运行一次查询,下一个foreach
再次运行它。如果查询很昂贵,那么ToList()
方法(以及存储该列表)可以节省大量资金。
答案 1 :(得分:9)
不,没有区别。 in
表达式计算一次。更具体地说,foreach
构造在GetEnumerator()
表达式上调用in
方法并重复调用MoveNext()
并访问Current
属性以遍历IEnumerable
1}}。
OTOH,呼叫ToList()
是多余的。你不应该打扰它。
如果输入是数据库,情况略有不同,因为LINQ输出IQueryable
,但我很确定foreach
仍然将其视为IEnumerable
({ {1}}继承)。
答案 2 :(得分:2)
正如所写的那样,循环的每次迭代都会完成与获取下一个结果所需的工作量相同的工作量。所以答案在技术上应该是“以上都不是”。查询将“分段”执行。
如果您使用ToList()
或任何其他具体化方法(ToArray()
等),那么将在现场 和后续操作(例如迭代结果)只会在“哑”列表上工作。
如果numbers
是IQueryable
而不是IEnumerable
- 因为它可能出现在数据库场景中 - 那么上述内容仍然接近真相,尽管不是很完美准确的描述。特别是,在第一次实现结果的实现时,可查询提供者将与数据库通信并生成结果集;然后,每次迭代都会拉出此结果集中的行。
答案 3 :(得分:1)
linq查询将在枚举时执行(作为.ToList()
调用的结果或对结果执行foreach
。
如果要枚举linq查询的结果两次,则两次都会使它查询数据源(在您的示例中,枚举集合),因为它本身只返回IEnumerable
。但是,根据linq查询,它可能并不总是枚举整个集合(例如.Any()
和.Single()
将在第一个对象或第一个匹配对象上停止(如果有.Where()
)
linq提供程序的实现细节可能不同,因此数据源是数据库时的通常行为是直接调用.ToList()
cache
查询结果&amp;还要确保查询(在EF,L2S或NHibernate的情况下)一次执行然后执行而不是在代码稍后的某个时刻枚举集合并防止查询被执行如果多次枚举结果,则多次。