假设我有以下课程:
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
为什么两个方法体不同? Aren他们完全一样吗?
此外,我猜测32
中的前两个字节1
和dynamicMethodBody
与将常量1
加载到评估堆栈有关,但为什么这两个字节不存在methodBody
?
答案 0 :(得分:2)
如果在调试模式下编译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有一个操作码。