假设我有以下类:
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
使用反射创建我自己的方法。发出:
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
中的前两个字节32
和1
与将常量1
加载到评估堆栈有关,但为什么methodBody
中不存在这两个字节?
如果您在调试模式下编译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(1 0 0 0)的4字节,然后是Ret(42)。
所以你的代码比编译器在发布模式下生成的代码要冗长一些,因为有一个内置的32位整数1的操作码。