为什么LINQ到对象方法的顺序很重要

本文关键字:顺序 方法 LINQ 对象 为什么 | 更新日期: 2023-09-27 18:18:37

我读了这个问题的答案,它解释了LINQ到对象方法的顺序有什么不同。我的问题是为什么?

如果我写一个LINQ to SQL查询,它与LINQ方法的顺序无关- projections例如:

session.Query<Person>().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时,所有的投影都可以按合理的顺序放置(例如:

为什么LINQ到对象方法的顺序很重要

为什么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放在首位,您可以有效地将其嵌套到最内部的方法调用中。这意味着在枚举结果时将对整个集合进行排序。如果你要改变顺序:

   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 object的延迟执行与Linq to sql的(和EF的)不同。

对于link -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不会重新排序,以避免可能的运行时步骤去做一些应该在编码时优化的事情。世界上的resharpers可能会在某个时候引入代码分析工具来找出这样的优化机会,但这绝对不是运行时的工作。