- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
无论我使用表达式查询语法还是方法语法,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逐步查看其工作原理的说明:
Method arguments arg1 through argN are pushed onto the stack.
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.
The return value is pushed onto the stack.
所以在调用方法之前,它的参数被压入堆栈。在您的第一个代码中,它是这样的:
_employees
入栈,推 Func<Employee, bool>
到堆栈上Where
local 0
中local 0
(将其插入堆栈),推 Func<Employee, string>
到堆栈上OrderByDescending
local 1
中local 1
, 推送 Func<Employee, string>
到堆栈上Select
local2
中第二个版本中没有加载本地的东西,因为方法调用的返回值已经存储在堆栈中。当一个方法被调用时,你有返回值,它将成为下一次调用的第一个参数,只有作为第二个参数的委托(delegate)被压入堆栈并调用下一个方法。
我还应该提一下,这根本不是 LINQ 特有的。方法链接以这种方式工作,LINQ 并不特殊。
注意:我已经简化了推送委托(delegate)的步骤,其中的额外代码用于缓存。委托(delegate)实例缓存在编译器生成的类的字段中,在创建新的委托(delegate)实例之前,编译器会检查它是否在之前创建以提高效率。
关于c# - LINQ 序列 - 它们如何链接到 IL 中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52285526/
我正在开发一个 voip 调用应用程序。我需要做的是在接到来电时将 Activity 带到前台。我在应用程序中使用 Twilio,并在收到推送消息时开始调用。 问题是我试图在接到任何电话时显示 Act
我是一名优秀的程序员,十分优秀!