了解 CIL 和 ldelem.ref 的工作原理

Understanding CIL & the workings of ldelem.ref

我想解释一下 ldelem.ref 的作用。到目前为止,我已经将索引处的元素作为 O.

加载到堆栈顶部

索引是什么?我认为类型 O 意味着对象的类型将保持不变,例如,如果它是一个字符串,它将保持一个字符串。

下面是我正在处理的一些代码的示例,非常感谢您的理解。我评论了我相信我知道的。 所以在这种情况下是

.locals init (
    string V_0,
    bool V_1,
    string V_2,
    bool V_3,
    string V_4,
    string V_5,
    string V_6)              // Declared 6 variables

.try 
{
    IL_0000:  nop
    IL_0001:  nop                   // Does nothing - Debug build
    IL_0002:  ldarg.0               // Loads Argument 0 into memory/stack
    IL_0003:  ldc.i4.0              // Push Constant Value 0 into memory [Possibly from a variable]
    IL_0004:  ldelem.ref            // Loads element at index onto the top of the stack as an O
    IL_0005:  stloc.0               // Pop value from stack into local Variable 0
    IL_0006:  ldloc.0               // Load local variable 0 onto stack
    IL_0007:  ldstr      "del"      // Loads string "del" in to top of stack
    IL_000c:  call       bool [mscorlib]System.String::op_Equality(string, string)  // Compares strings to see if they are equal
    IL_0011:  stloc.1               // Pop value from stack into local variable 1
    IL_0012:  ldloc.1               // Load local variable 1 onto the stack
    IL_0013:  brfalse.s  IL_004e    // If variable 1 is true keep going else jump to IL_004e

ldelem.ref 在这里做什么? op_Equality 是在比较字符串 "del" 和变量 0 的内容吗? 我认为调用完成后,操作的布尔值然后存储在堆栈的顶部并且 stloc.1 弹出布尔值并将其存储在变量 1 中,然后 ldloc.1 加载将该变量压入堆栈并 brfalse.s 检查 Bool 值,如果 "jumps" 为 IL_004e 为假,是这种情况吗?

ldelem.ref 将对数组元素的引用压入堆栈。这意味着它制作了它的副本,而不是实际存储的引用。阅读 System.Reflection.Emit documentation 以了解更多信息可能会有用。

另一件可能有助于理解的事情是,每条 MSIL 指令都需要来自堆栈的 N 个值,其中 N 由所使用的特定指令(除其他事项外)决定。这可以用来在心理上对事物进行分组以便于理解。

IL_0002:  ldarg.0               
IL_0003:  ldc.i4.0              
IL_0004:  ldelem.ref 

ldelem.ref 要求堆栈按顺序具有:数组引用,该数组的索引。它将弹出这些值,然后将引用压入堆栈。堆栈现在只包含一个东西。

IL_0005:  stloc.0 

堆栈上的唯一值现在被弹出并放入本地存储。堆栈为空。

IL_0006:  ldloc.0               
IL_0007:  ldstr      "del"      
IL_000c:  call       bool [mscorlib]System.String::op_Equality(string, string)  

调用指令将在堆栈中弹出与参数一样多的项(如果调用的方法是实例方法,则包括实例)。在这种情况下,它需要两个字符串参数,并将弹出局部变量的内容以及文字字符串 "del"。静态方法op_Equality returns 一个bool,这样就会被压入栈中。堆栈现在只包含一个东西。

IL_0011:  stloc.1               

堆栈上的唯一值现在被弹出并放入本地存储。堆栈为空。

IL_0012:  ldloc.1               
IL_0013:  brfalse.s  IL_004e    

然后加载值并应用分支逻辑。

在 C# 中,MSIL 正在执行与以下内容等效的操作:

if (array[0] == "del")

MSIL 中的一切都是一种平衡行为。由于这是一个调试版本,与优化版本相比,可能有更多的 nop 指令和更多的局部变量使用(这对你有好处,因为你可以更容易地理解事情)。