为什么LINQ to objects方法的顺序很重要

我读了这个问题的答案,解释了LINQ to objects方法的顺序有所不同。 我的问题是为什么?

如果我编写LINQ to SQL查询,那么LINQ方法的顺序并不重要 – 例如:

 session.Query().OrderBy(x => x.Id) .Where(x => x.Name == "gdoron") .ToList(); 

表达式树将转换为合理的SQL,如下所示:

  SELECT * FROM Persons WHERE Name = 'gdoron' ORDER BY Id; 

当我运行查询时,无论方法的顺序多么奇怪,SQL查询都会根据表达式树构建。
为什么它与LINQ to objects不一样?
当我枚举一个IQueryable时,所有的投影都可以放在一个合理的顺序中(例如,在Where之后的Order By)就像数据库优化器那样。

为什么LINQ对象不能以这种方式工作?

LINQ to Objects不使用表达式树。 该语句直接转换为一系列方法调用,每个方法调用都作为普通的C#方法运行。

因此,LINQ to Objects中的以下内容:

  var results = collection.OrderBy(x => x.Id) .Where(x => x.Name == "gdoron") .ToList(); 

变成直接方法调用:

  var results = Enumerable.ToList( Enumerable.Where( Enumerable.OrderBy(collection, x => x.Id), x => x.Name = "gdoron" ) ); 

通过查看方法调用,您可以看到为什么排序很重要。 在这种情况下,通过首先放置OrderBy,您可以有效地将其嵌套到最内部的方法调用中。 这意味着在枚举resutls时将订购整个集合。 如果您要切换订单:

  var results = collection .Where(x => x.Name == "gdoron") .OrderBy(x => x.Id) .ToList(); 

然后生成的方法链切换到:

  var results = Enumerable.ToList( Enumerable.OrderBy( Enumerable.Where(collection, x => x.Name = "gdoron"), x => x.Id ) ); 

反过来,这意味着只有过滤后的结果才需要在OrderBy执行时进行排序。

Linq to objects的延迟执行与linq-to-sql(和EF)的工作方式不同。

使用linq-to-objects,方法链将按列出方法的顺序执行 – 它不使用表达式树来存储和翻译整个事物。

调用OrderBy 然后使用linq-to-objects将枚举结果时,对集合进行排序, 然后对其进行过滤。 相反, Where使用OrderBy对其进行排序之前调用Where过滤结果将在您枚举时首先过滤,然后排序。 因此,后一种情况可能会产生巨大的差异,因为您可能会排序更少的项目。

因为,使用LINQ for SQL,SELECT的SQL语法要求不同的子句以特定的顺序出现。 编译器必须生成语法正确的SQL。

在IEnumerable上对对象应用LINQ涉及迭代IEnumerable并对IEnumerable中的每个对象应用一系列动作。 顺序很重要:某些操作可能会转换对象(或对象本身的流),其他操作可能会抛弃对象(或将新对象注入流中)。

编译器无法确定您的意图。 它构建的代码可以按照您所说的顺序执行您所说的操作。

使用副作用操作是完全合法的。 相比:

 "crabapple" .OrderBy(c => { Console.Write(c); return c; }) .Where(c => { Console.Write(c); return c > 'c'; }) .Count(); "crabapple" .Where(c => { Console.Write(c); return c > 'c'; }) .OrderBy(c => { Console.Write(c); return c; }) .Count(); 

Linq to Objects不会重新排序,以避免在运行时执行某些应该在编码时优化的操作。 世界各地的解决方案可能会在某些时候引入代码分析工具来抽出这样的优化机会,但它绝对不是运行时的工作。