gpt4 book ai didi

c# - LINQ 序列 - 它们如何链接到 IL 中?

转载 作者:太空狗 更新时间:2023-10-30 00:59:53 24 4
gpt4 key购买 nike

无论我使用表达式查询语法还是方法语法,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 指令?

最佳答案

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

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

它存储在计算堆栈中。并使用评估堆栈中的值调用下一个方法。在调用链中的最后一个方法后,结果将设置回局部变量 names .

看看 Call逐步查看其工作原理的说明:

  1. Method arguments arg1 through argN are pushed onto the stack.

  2. Method arguments arg1 through argN are popped from the stack; the method call is performed with these arguments and control is transferred to the method referred to by the method descriptor. When complete, a return value is generated by the callee method and sent to the caller.

  3. The return value is pushed onto the stack.

所以在调用方法之前,它的参数被压入堆栈。在您的第一个代码中,它是这样的:

  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

第二个版本中没有加载本地的东西,因为方法调用的返回值已经存储在堆栈中。当一个方法被调用时,你有返回值,它将成为下一次调用的第一个参数,只有作为第二个参数的委托(delegate)被压入堆栈并调用下一个方法。

我还应该提一下,这根本不是 LINQ 特有的。方法链接以这种方式工作,LINQ 并不特殊。

注意:我已经简化了推送委托(delegate)的步骤,其中的额外代码用于缓存。委托(delegate)实例缓存在编译器生成的类的字段中,在创建新的委托(delegate)实例之前,编译器会检查它是否在之前创建以提高效率。

关于c# - LINQ 序列 - 它们如何链接到 IL 中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52285526/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com