微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

c# – LINQ实际编译的是什么?

背景

这样做的背景是我最近在评论中与另一位知识渊博的用户讨论了如何编译LINQ.我首先“总结”并说LINQ被编译为for循环.虽然这是不正确的,但我对其他堆栈(如this one)的理解是LINQ查询被编译为lambda,其中包含一个循环.然后在第一次枚举变量时调用它(之后存储结果).另一位用户表示LINQ需要额外的优化,例如散列.我找不到支持反对的任何支持文档.

我知道这似乎是一个非常模糊的观点,但我一直觉得,如果我不理解某些东西是如何完全运作的,那么我很难理解为什么我没有正确使用它.

问题

所以,让我们采取以下非常简单的例子:

var productNames = 
    from p in products 
    where p.Id > 100 and p.Id < 5000
    select p.ProductName;

在CLR中实际编译的这个语句是什么? LINQ接管了什么优化只是编写一个手动解析结果的函数?这只是语义还是还有更多呢?

澄清

显然我问的是这个问题,因为我不明白LINQ“黑匣子”的内部是什么样的.尽管我理解LINQ很复杂(而且功能强大),但我主要是在寻找对CLR或与LINQ语句等效的功能的基本理解.有很多很棒的站点可以帮助理解如何创建LINQ语句,但是很少有这些站点可以提供有关如何实际编译或运行这些语句的任何指导.

附注 – 我将完全阅读关于linq对象的John Skeet系列.

附注2 – 我不应该将其标记为LINQ to sql.我理解ORM和微观ORM是如何工作的.这真的是问题的重点.

解决方法

对于LINQ to Objects,它被编译成一组静态方法调用
var productNames = 
    from p in products 
    where p.Id > 100 and p.Id < 5000
    select p.ProductName;

变为:

IEnumerable<string> productNames = products
                                       .Where(p => p.Id > 100 and p.Id < 5000)
                                       .Select(p => p.ProductName);

这使用了Enumerable类型中定义的扩展方法,因此实际编译为:

IEnumerable<string> productNames = 
     Enumerable.Select(
        Enumerable.Where(products,p => p.Id > 100 and p.Id < 5000),p => p.ProductName
     );

处理此问题的lambda表达式由编译器转换为方法.将where中的lambda转换为一个方法,该方法可以设置为Func< Product,Boolean>,并选择为Func< Product,String>.

有关详细说明,请参阅Jon Skeet’s blog series: Reimplementing LINQ to Objects.他将详细介绍其工作原理,包括编译器转换(从查询语法到方法调用),方法实现方式等.

注意,LINQ to sql和IQueryable< T>实现是不同的.表达式< T>由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;
    }

获取编译为以下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>,!!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 
}

请注意,这些是方法调用的正常调用指令. lambdas转换为其他方法,例如:

[CompilerGenerated]
private static bool <ProductNames>b__2(Product p)
{
    return ((p.Id > 100) && (p.Id < 0x1388));
}

原文地址:https://www.jb51.cc/csharp/92665.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐