LINQ序列-如何在IL中链接?

时间:2018-09-11 23:41:25

标签: c# linq

无论我使用表达式查询语法还是方法语法,IL看起来几乎都是相同的。

以“渐进式语法”进行LINQ查询:

IEnumerable<Employee> query1 = _employees.Where(e => e.Location.Equals("California"));
IEnumerable<Employee> query2 = query1.OrderByDescending(e => e.Name);
IEnumerable<string> query3 = query2.Select(e => e.Name);

生成:

IL_0001: ldarg.0      // this
IL_0002: ldfld        class [mscorlib]System.Collections.Generic.IList`1<class Fundamentals.LINQ.Employee> Fundamentals.LINQ.Syntax::_employees
IL_0007: ldsfld       class [mscorlib]System.Func`2<class Fundamentals.LINQ.Employee, bool> Fundamentals.LINQ.Syntax/'<>c'::'<>9__3_0'
IL_000c: dup          
IL_000d: brtrue.s     IL_0026
IL_000f: pop          
IL_0010: ldsfld       class Fundamentals.LINQ.Syntax/'<>c' Fundamentals.LINQ.Syntax/'<>c'::'<>9'
IL_0015: ldftn        instance bool Fundamentals.LINQ.Syntax/'<>c'::'<TestMethodSyntaxProgressive>b__3_0'(class Fundamentals.LINQ.Employee)
IL_001b: newobj       instance void class [mscorlib]System.Func`2<class Fundamentals.LINQ.Employee, bool>::.ctor(object, native int)
IL_0020: dup          
IL_0021: stsfld       class [mscorlib]System.Func`2<class Fundamentals.LINQ.Employee, bool> Fundamentals.LINQ.Syntax/'<>c'::'<>9__3_0'
IL_0026: call         class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0/*class Fundamentals.LINQ.Employee*/> [System.Core]System.Linq.Enumerable::Where<class Fundamentals.LINQ.Employee>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0/*class Fundamentals.LINQ.Employee*/>, class [mscorlib]System.Func`2<!!0/*class Fundamentals.LINQ.Employee*/, bool>)
IL_002b: stloc.0      // query1

// [100 13 - 100 82]
IL_002c: ldloc.0      // query1
IL_002d: ldsfld       class [mscorlib]System.Func`2<class Fundamentals.LINQ.Employee, string> Fundamentals.LINQ.Syntax/'<>c'::'<>9__3_1'
IL_0032: dup          
IL_0033: brtrue.s     IL_004c
IL_0035: pop          
IL_0036: ldsfld       class Fundamentals.LINQ.Syntax/'<>c' Fundamentals.LINQ.Syntax/'<>c'::'<>9'
IL_003b: ldftn        instance string Fundamentals.LINQ.Syntax/'<>c'::'<TestMethodSyntaxProgressive>b__3_1'(class Fundamentals.LINQ.Employee)
IL_0041: newobj       instance void class [mscorlib]System.Func`2<class Fundamentals.LINQ.Employee, string>::.ctor(object, native int)
IL_0046: dup          
IL_0047: stsfld       class [mscorlib]System.Func`2<class Fundamentals.LINQ.Employee, string> Fundamentals.LINQ.Syntax/'<>c'::'<>9__3_1'
IL_004c: call         class [System.Core]System.Linq.IOrderedEnumerable`1<!!0/*class Fundamentals.LINQ.Employee*/> [System.Core]System.Linq.Enumerable::OrderByDescending<class Fundamentals.LINQ.Employee, string>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0/*class Fundamentals.LINQ.Employee*/>, class [mscorlib]System.Func`2<!!0/*class Fundamentals.LINQ.Employee*/, !!1/*string*/>)
IL_0051: stloc.1      // query2

// [101 13 - 101 69]
IL_0052: ldloc.1      // query2
IL_0053: ldsfld       class [mscorlib]System.Func`2<class Fundamentals.LINQ.Employee, string> Fundamentals.LINQ.Syntax/'<>c'::'<>9__3_2'
IL_0058: dup          
IL_0059: brtrue.s     IL_0072
IL_005b: pop          
IL_005c: ldsfld       class Fundamentals.LINQ.Syntax/'<>c' Fundamentals.LINQ.Syntax/'<>c'::'<>9'
IL_0061: ldftn        instance string Fundamentals.LINQ.Syntax/'<>c'::'<TestMethodSyntaxProgressive>b__3_2'(class Fundamentals.LINQ.Employee)
IL_0067: newobj       instance void class [mscorlib]System.Func`2<class Fundamentals.LINQ.Employee, string>::.ctor(object, native int)
IL_006c: dup          
IL_006d: stsfld       class [mscorlib]System.Func`2<class Fundamentals.LINQ.Employee, string> Fundamentals.LINQ.Syntax/'<>c'::'<>9__3_2'
IL_0072: call         class [mscorlib]System.Collections.Generic.IEnumerable`1<!!1/*string*/> [System.Core]System.Linq.Enumerable::Select<class Fundamentals.LINQ.Employee, string>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0/*class Fundamentals.LINQ.Employee*/>, class [mscorlib]System.Func`2<!!0/*class Fundamentals.LINQ.Employee*/, !!1/*string*/>)
IL_0077: stloc.2      // query3

根据LINQ的工作方式,Where()操作的输出序列应成为OrderByDescending()的输入序列,而第二个输出序列应流入Select()。我只是不明白哪些IL代码与之相对应。

这是神奇的部分吗?

IL_002b: stloc.0      // query1

// [100 13 - 100 82]
IL_002c: ldloc.0      // query1

如果我以“流利”的方式执行LINQ查询:

IEnumerable<string> names = _employees.Where(e => e.Location.Equals("California")).OrderByDescending(e => e.Name).Select(e => e.Name);

我没有得到stloc.0,ldloc.0指令。这是“渐进语法”和“流利语法”之间发出的IL的唯一区别。 JIT编译器中是否还有第二步生成额外的IL指令来执行序列输入?

1 个答案:

答案 0 :(得分:5)

两个版本之间的唯一区别是本地语言。在第一个版本中,每个调用的返回值都保存在本地中,然后在该本地上调用下一个方法,依此类推。

我假设您要问的是在没有本地人的情况下如何工作?方法的返回值存储在哪里?

它存储在评估堆栈中。然后使用评估堆栈中的值调用下一个方法。在链中调用了最后一个方法之后,结果将重新设置为局部变量names

看看Call指令以逐步了解它的工作原理:

  
      
  1. 方法参数arg1到argN被压入堆栈。

  2.   
  3. 方法参数arg1到argN从堆栈中弹出;的   使用这些参数执行方法调用,并且控制为   转移到方法描述符所引用的方法。什么时候   完成后,被调用方方法会生成并返回一个返回值   给呼叫者。

  4.   
  5. 返回值被压入堆栈。

  6.   

因此,在调用方法之前,会将其参数压入堆栈。在您的第一个代码中,它是这样的:

  1. _employees推入堆栈,将Func<Employee, bool>推入堆栈
  2. 致电Where
  3. 将结果存储在local 0
  4. 加载local 0(将其推入堆栈),将Func<Employee, string>推入堆栈
  5. 致电OrderByDescending
  6. 将结果存储在local 1
  7. 加载local 1,将Func<Employee, string>推入堆栈
  8. 致电Select
  9. 将结果存储在local2

第二个版本中没有加载本地内容,因为方法调用的返回值已存储在堆栈中。调用方法时,您将拥有返回值,该返回值将成为下一个调用的第一个参数,只有作为第二个参数的委托才被压入堆栈,然后调用下一个方法。

我还应该提到这并非完全针对LINQ。方法链接以这种方式工作,LINQ并不特殊。

注意:我简化了委托步骤,这里有用于缓存的额外代码。在创建新的委托实例编译器之前,将委托实例缓存在编译器生成的类的字段中,以检查效率是否在以前创建。