【问题标题】:Different IL codes for same method body同一方法体的不同 IL 代码
【发布时间】:2015-10-22 00:48:18
【问题描述】:

假设我有以下课程:

public class SomeClass
{
    public int GetValue()
    {
        return 1;
    }
}

检查此方法生成的 IL 代码:

byte[] methodBody = typeof(SomeClass).GetMethod("GetValue").GetMethodBody().GetILAsByteArray();

我们知道methodBody 是:

[0, 23, 10, 43, 0, 6, 42] -- 7 bytes

使用Reflection.Emit 创建我自己的方法:

MethodBuilder methodBuilder = typeBuilder.DefineMethod("GetValue", MethodAttributes.Public, typeof(int), Type.EmptyTypes);
ILGenerator il = methodBuilder.GetILGenerator();
il.Emit(OpCodes.Ldc_I4, 1);
il.Emit(OpCodes.Ret);

//....

byte[] dynamicMethodBody = dynamicType.GetMethod("GetValue").GetMethodBody().GetILAsByteArray();

我们知道dynamicMethodBody 是:

[32, 1, 0, 0, 0, 42] -- 6 bytes

为什么两个方法体不同?它们不完全一样吗?

此外,我猜我的dynamicMethodBody 中的前两个字节321 与将常量1 加载到评估堆栈有关,但是为什么这两个字节不存在在methodBody?

【问题讨论】:

    标签: c# .net reflection cil reflection.emit


    【解决方案1】:

    如果你在调试模式下编译 SomeClass,编译器会插入很多额外的东西,只是为了让调试体验更好。事实证明,优化的 IL 在简单的情况下更容易阅读。

    对于编译器生成的主体,我相信它会生成一个 noop (0)、Ldc_I4_1 (23),然后是一些存储在本地和一个分支指令(我不确定我是否遵循),然后是一个 Ret (42)。这是基于debug中的反编译代码:

    IL_0000: nop
    IL_0001: ldc.i4.1
    IL_0002: stloc.0
    IL_0003: br.s IL_0005
    IL_0005: ldloc.0
    IL_0006: ret
    

    您可以在反编译的发布代码中看到说明要简单得多:

    IL_0000: ldc.i4.1
    IL_0001: ret
    

    我相信dynamicMethodBody 对于 Ldc_I4 (32) 是 1 个字节,对于整数 1 是 4 个字节,(1 0 0 0),然后是 Ret (42)。

    因此,您的代码比编译器在发布模式下生成的代码要冗长一些,因为内置常量 32 位整数 1 的操作码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-10
      • 1970-01-01
      相关资源
      最近更新 更多