背景
这样做的背景是我最近在评论中与另一位知识渊博的用户讨论了如何编译LINQ.我首先"总结"并说LINQ被编译为for循环.虽然这是不正确的,我从其他堆栈如理解这一个是LINQ查询被编译到拉姆达与它的内部循环.然后在第一次枚举变量时调用它(之后存储结果).另一位用户表示LINQ需要额外的优化,例如散列.我找不到支持或反对的任何支持文档.
我知道这似乎是一个非常模糊的观点,但我一直觉得,如果我不理解某些东西是如何完全运作的,那么我很难理解为什么我没有正确使用它.
问题
所以,让我们采取以下非常简单的例子:
var productNames =
from p in products
where p.Id > 100 and p.Id < 5000
select p.ProductName;
Run Code Online (Sandbox Code Playgroud)
在CLR中实际编译的这个语句是什么?LINQ接管了什么优化只是编写一个手动解析结果的函数?这只是语义还是还有更多呢?
澄清
显然我问的是这个问题,因为我不明白LINQ"黑匣子"的内部是什么样的.尽管我理解LINQ很复杂(而且功能强大),但我主要是在寻找对CLR或与LINQ语句等效的功能的基本理解.有很多很棒的站点可以帮助理解如何创建LINQ语句,但是很少有这些站点可以提供有关如何实际编译或运行这些语句的任何指导.
附注 - 我将完全阅读关于linq对象的John Skeet系列.
附注2 - 我不应该将其标记为LINQ to SQL.我理解ORM和微观ORM是如何工作的.这真的是问题的重点.
Ree*_*sey 13
对于LINQ to Objects,它被编译成一组静态方法调用:
var productNames =
from p in products
where p.Id > 100 and p.Id < 5000
select p.ProductName;
Run Code Online (Sandbox Code Playgroud)
变为:
IEnumerable<string> productNames = products
.Where(p => p.Id > 100 and p.Id < 5000)
.Select(p => p.ProductName);
Run Code Online (Sandbox Code Playgroud)
这使用了Enumerable
类型中定义的扩展方法,因此实际编译为:
IEnumerable<string> productNames =
Enumerable.Select(
Enumerable.Where(products, p => p.Id > 100 and p.Id < 5000),
p => p.ProductName
);
Run Code Online (Sandbox Code Playgroud)
处理此问题的lambda表达式由编译器转换为方法.lambda在哪里变成了一个方法,可以设置为a Func<Product, Boolean>
,并选择成a Func<Product, String>
.
有关详细说明,请参阅Jon Skeet的博客系列:重新实现LINQ to Objects.他将介绍其工作原理的整个过程,包括编译器转换(从查询语法到方法调用),方法的实现方式等.
请注意,LINQ to Sql和IQueryable<T>
实现是不同的.该Expression<T>
是由氧产生被传递到查询提供商,而这又是在某种方式(这取决于供应商如何做到这一点)呼叫中"转化",通常在ORM的情况下,在服务器上运行.
对于此方法,例如:
private static IEnumerable<string> ProductNames(IEnumerable<Product> products)
{
var productNames =
from p in products
where p.Id > 100 && p.Id < 5000
select p.ProductName;
return productNames;
}
Run Code Online (Sandbox Code Playgroud)
获取编译为以下IL:
.method private hidebysig static class [mscorlib]System.Collections.Generic.IEnumerable`1<string> ProductNames(class [mscorlib]System.Collections.Generic.IEnumerable`1<class ConsoleApplication3.Product> products) cil managed
{
.maxstack 3
.locals init (
[0] class [mscorlib]System.Collections.Generic.IEnumerable`1<string> enumerable,
[1] class [mscorlib]System.Collections.Generic.IEnumerable`1<string> enumerable2)
L_0000: nop
L_0001: ldarg.0
L_0002: ldsfld class [mscorlib]System.Func`2<class ConsoleApplication3.Product, bool> ConsoleApplication3.Program::CS$<>9__CachedAnonymousMethodDelegate3
L_0007: dup
L_0008: brtrue.s L_001d
L_000a: pop
L_000b: ldnull
L_000c: ldftn bool ConsoleApplication3.Program::<ProductNames>b__2(class ConsoleApplication3.Product)
L_0012: newobj instance void [mscorlib]System.Func`2<class ConsoleApplication3.Product, bool>::.ctor(object, native int)
L_0017: dup
L_0018: stsfld class [mscorlib]System.Func`2<class ConsoleApplication3.Product, bool> ConsoleApplication3.Program::CS$<>9__CachedAnonymousMethodDelegate3
L_001d: call class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0> [System.Core]System.Linq.Enumerable::Where<class ConsoleApplication3.Product>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, class [mscorlib]System.Func`2<!!0, bool>)
L_0022: ldsfld class [mscorlib]System.Func`2<class ConsoleApplication3.Product, string> ConsoleApplication3.Program::CS$<>9__CachedAnonymousMethodDelegate5
L_0027: dup
L_0028: brtrue.s L_003d
L_002a: pop
L_002b: ldnull
L_002c: ldftn string ConsoleApplication3.Program::<ProductNames>b__4(class ConsoleApplication3.Product)
L_0032: newobj instance void [mscorlib]System.Func`2<class ConsoleApplication3.Product, string>::.ctor(object, native int)
L_0037: dup
L_0038: stsfld class [mscorlib]System.Func`2<class ConsoleApplication3.Product, string> ConsoleApplication3.Program::CS$<>9__CachedAnonymousMethodDelegate5
L_003d: call class [mscorlib]System.Collections.Generic.IEnumerable`1<!!1> [System.Core]System.Linq.Enumerable::Select<class ConsoleApplication3.Product, string>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, class [mscorlib]System.Func`2<!!0, !!1>)
L_0042: stloc.0
L_0043: ldloc.0
L_0044: stloc.1
L_0045: br.s L_0047
L_0047: ldloc.1
L_0048: ret
}
Run Code Online (Sandbox Code Playgroud)
请注意,这些是call
方法调用的常规指令.lambdas转换为其他方法,例如:
[CompilerGenerated]
private static bool <ProductNames>b__2(Product p)
{
return ((p.Id > 100) && (p.Id < 0x1388));
}
Run Code Online (Sandbox Code Playgroud)