C# LINQ实际上编译为什么?
背景 这样做的背景是,我最近在评论中与另一位知识渊博的用户讨论了LINQ是如何编译的。我首先“总结”了一下,并说LINQ被编译成了一个for循环。虽然这是不正确的,但我从其他堆栈(例如LINQ查询)得到的理解是,LINQ查询被编译成一个lambda,其中包含一个循环。然后在第一次枚举变量时调用该函数(之后存储结果)。另一个用户说LINQ需要额外的优化,比如散列。我找不到任何支持或反对此的支持文档 我知道这似乎是一个非常模糊的观点,但我一直觉得,如果我不完全理解某些东西是如何工作的,那么就很难理解为什么我没有正确地使用它 问题 因此,让我们举一个非常简单的例子:C# LINQ实际上编译为什么?,c#,linq,clr,C#,Linq,Clr,背景 这样做的背景是,我最近在评论中与另一位知识渊博的用户讨论了LINQ是如何编译的。我首先“总结”了一下,并说LINQ被编译成了一个for循环。虽然这是不正确的,但我从其他堆栈(例如LINQ查询)得到的理解是,LINQ查询被编译成一个lambda,其中包含一个循环。然后在第一次枚举变量时调用该函数(之后存储结果)。另一个用户说LINQ需要额外的优化,比如散列。我找不到任何支持或反对此的支持文档 我知道这似乎是一个非常模糊的观点,但我一直觉得,如果我不完全理解某些东西是如何工作的,那么就很难理解
var productNames =
from p in products
where p.Id > 100 and p.Id < 5000
select p.ProductName;
var产品名称=
产品中的p
其中p.Id>100且p.Id<5000
选择p.ProductName;
在CLR中,此语句实际编译为什么?仅仅编写一个手动解析结果的函数,LINQ就接管了我的哪些优化工作?这仅仅是语义学,还是有更多的意义
澄清
显然,我问这个问题是因为我不明白LINQ“黑匣子”的内部是什么样子。尽管我知道LINQ是复杂的(而且功能强大),但我主要是想对CLR或LINQ语句的函数等价物有一个基本的了解。有很多很好的网站可以帮助您理解如何创建LINQ语句,但其中很少有网站能够对如何编译或运行LINQ语句提供指导
旁注-我绝对会通读关于linq to对象的John Skeet系列
旁注2——我不应该将其标记为LINQ to SQL。我了解ORM和micro ORM的工作原理。这真的超出了问题的重点 对于LINQ to对象,这被编译成一组静态方法调用:
var productNames =
from p in products
where p.Id > 100 and p.Id < 5000
select p.ProductName;
编译器将处理此问题的lambda表达式转换为方法。where中的lambda被转换为一种方法,该方法可以设置为aFunc
,并将select设置为aFunc
有关详细说明,请参阅。他介绍了整个过程,包括编译器转换(从查询语法到方法调用)、方法如何实现等等
请注意,LINQ到Sql和IQueryable
的实现是不同的。lambda生成的表达式
被传递到查询提供程序中,查询提供程序又以某种方式(取决于提供程序如何实现)被“转换”为调用,通常在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;
}
查询语法只是方法语法的语法糖,它有效地编译为:
var productNames = Products().Where(p => p.Id > 100 && p.Id < 5000).Select(p => productName);
var-productNames=Products()。其中(p=>p.Id>100&&p.Id<5000)。选择(p=>productName);
现在,这些函数的实际功能取决于您使用的LINQ的风格,例如LINQ to Objects(在内存处理程序中链接在一起)或LINQ to SQL(将其转换为SQL查询)等。有关LINQ to Objects的详细说明,请参阅。@Hogan不,它不会…如果您使用类似于
连接,分组方式,Distinct
,ToLookup
等,当然可以。但是Where
根本不会使用基于散列的算法/结构。@Hogan通读了这些帖子。他在每一篇文章中都讨论了MS是如何实现它们的,以及他选择何时/为什么偏离它们的实现。@Hogan,:)但是.Where()和.Select()编译的目的是什么?CIL内部没有.Where()。请看:我正试图弄清楚这在低水平上解决了什么问题。这就是这个问题的真正目的@drew_w我回答说-它被转换成可枚举的。Where和Enumerable。Select方法调用。这个回答没有解决OP的核心问题这个语句在CLR中实际编译成什么?
@drew_w:这些是常规的静态函数。Jon Skeet向你展示了如何自己编写它们?@drew_w这只是语义学——我试图通过展示发生了什么来解释这一点,但它只是转换为方法调用,而方法调用(内部)大部分只是循环。使用IQueryable
,还有一些优化,查询可以在服务器上进行,而不是在本地进行,但使用LINQ to Objects,更多的是简化和更干净的使用。1)您并没有真正回答自己的问题。添加的说明清楚地表明,您对查询语法如何映射到方法语法并不感兴趣,而是对实际的Where
、Select
等方法的实现感兴趣。2) 在代码片段之后您所说的一切都不是编写的。每个linq到anythingButObjects提供程序都基于IQueryable
接口,以及Where
、Select
等方法。这些方法都是可查询的。Where
等,对于每个查询提供程序都不是不同的方法。这是查询提供程序对生成的表达式所做的不同操作。
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;
}
.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
}
[CompilerGenerated]
private static bool <ProductNames>b__2(Product p)
{
return ((p.Id > 100) && (p.Id < 0x1388));
}
var productNames = Products().Where(p => p.Id > 100 && p.Id < 5000).Select(p => productName);