为什么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 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可能会在某个时候引入代码分析工具来找出这样的优化机会,但这绝对不是运行时的工作。