Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/276.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# LINQ实际上编译为什么?_C#_Linq_Clr - Fatal编程技术网

C# LINQ实际上编译为什么?

C# LINQ实际上编译为什么?,c#,linq,clr,C#,Linq,Clr,背景 这样做的背景是,我最近在评论中与另一位知识渊博的用户讨论了LINQ是如何编译的。我首先“总结”了一下,并说LINQ被编译成了一个for循环。虽然这是不正确的,但我从其他堆栈(例如LINQ查询)得到的理解是,LINQ查询被编译成一个lambda,其中包含一个循环。然后在第一次枚举变量时调用该函数(之后存储结果)。另一个用户说LINQ需要额外的优化,比如散列。我找不到任何支持或反对此的支持文档 我知道这似乎是一个非常模糊的观点,但我一直觉得,如果我不完全理解某些东西是如何工作的,那么就很难理解

背景

这样做的背景是,我最近在评论中与另一位知识渊博的用户讨论了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被转换为一种方法,该方法可以设置为a
Func
,并将select设置为a
Func

有关详细说明,请参阅。他介绍了整个过程,包括编译器转换(从查询语法到方法调用)、方法如何实现等等

请注意,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);