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

时间:2018-12-27 23:59:31

标签: .net .net-assembly disassembly cil

我想解释一下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值,如果错误的“跳转”到IL_004e,是这种情况吗?

1 个答案:

答案 0 :(得分:2)

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返回布尔值,因此它将被压入堆栈。堆栈现在只包含一个东西。

IL_0011:  stloc.1               

现在将弹出堆栈上的唯一值,并将其放入本地存储中。堆栈是空的。

IL_0012:  ldloc.1               
IL_0013:  brfalse.s  IL_004e    

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

在C#中,MSIL所做的事情等同于以下内容:

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

MSIL中的所有内容都是一种平衡行为。由于这是调试版本,因此与优化版本相比,nop指令可能更多,对本地语言的使用也更多(这对您有好处,因为您可以更轻松地进行操作)。