foreach只执行一次查询吗?

我有一个项目列表和LINQ查询。 现在,使用LINQ的延迟执行,后续的foreach循环是否只执行一次查询或循环中的每次转弯?

给出这个例子(取自LINQ查询简介(C#),在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 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()) 

而且,如果底层数据不在数组中,而是在数据库中,这是否重要?

现在,使用LINQ的延迟执行,后续的foreach循环是否只执行一次查询或循环中的每次转弯?

是的,一次循环。 实际上,它可以执行少于一次的查询 – 您可以中止循环部分,并且不会对任何剩余项执行(num % 2) == 0测试。

或者,换句话说,如果我有以下情况会有什么不同:

 foreach (int num in numQuery.ToList()) 

两个区别:

  1. 在上面的例子中, ToList()浪费时间和内存,因为它首先与初始foreach做同样的事情,从中构建一个列表,然后foreach s该列表。 根据结果​​的大小,差异将介于微不足道和防止代码工作之间。

  2. 但是,如果你要反复对相同的结果做foreach ,或者反复使用它,那么当foreach只运行一次查询时,下一个foreach再次运行它。 如果查询很昂贵,那么ToList()方法(并存储该列表)可以节省大量资金。

不,它没有任何区别。 in表达式计算一次。 更具体地说, foreach构造在in表达式上调用GetEnumerator()方法,并重复调用MoveNext()并访问Current属性以遍历IEnumerable

OTOH,调用ToList()是多余的。 你不应该打扰它。

如果输入是数据库,情况稍有不同,因为LINQ输出IQueryable ,但我很确定foreach仍将它视为IEnumerableIQueryableinheritance)。

如上所述,循环的每次迭代都会完成与获取下一个结果所需的工作量相同的工作量。 所以答案在技术上应该是“以上都不是”。 查询将“分段”执行。

如果您使用ToList()或任何其他实现方法( ToArray()等),那么查询将在现场进行一次评估并且后续操作(例如迭代结果)将仅在“哑”列表上工作。

如果numbersIQueryable而不是IEnumerable – 因为它可能在数据库场景中 – 那么上面仍然接近事实,尽管不是完全准确的描述。 特别是,在第一次实现结果的实现时,可查询提供者将与数据库通信并生成结果集; 然后,每次迭代都会拉出此结果集中的行。

linq查询将在枚举时执行(作为.ToList()调用的结果或对结果执行foreach

如果要枚举linq查询的结果两次,则两次都会使它查询数据源(在您的示例中,枚举集合),因为它本身只返回IEnumerable 。 但是,根据linq查询,它可能并不总是枚举整个集合(例如.Any().Single()将停止在第一个对象或第一个匹配对象(如果有.Where() )。

linq提供程序的实现细节可能不同,因此数据源是数据库时的通常行为是直接调用.ToList()cache查询结果并确保查询(在EF,L2S的情况下)或者NHibernate)在那里执行,然后在代码中稍后的某个时刻枚举集合,并且如果多次枚举结果,则防止查询被多次执行。