Sel*_*enç 12 c# cil reflection.emit ilgenerator
我有这样的代码,发出一些IL调用指令string.IndexOf一对null对象:
MethodBuilder methodBuilder = typeBuilder.DefineMethod(
"Foo",
MethodAttributes.Public,
typeof(void), Array.Empty<Type>());
var methodInfo = typeof(string).GetMethod("IndexOf", new[] {typeof(char)});
ILGenerator ilGenerator = methodBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldnull);
ilGenerator.Emit(OpCodes.Ldc_I4_S, 120);
ilGenerator.Emit(OpCodes.Call, methodInfo);
ilGenerator.Emit(OpCodes.Ret);
Run Code Online (Sandbox Code Playgroud)
这是生成的IL代码:
.method public instance int32 Foo() cil managed
{
// Code size 12 (0xc)
.maxstack 2
IL_0000: ldnull
IL_0001: ldc.i4.s 120
IL_0003: nop
IL_0004: nop
IL_0005: nop
IL_0006: call instance int32 [mscorlib]System.String::IndexOf(char)
IL_000b: ret
} // end of method MyDynamicType::Foo
Run Code Online (Sandbox Code Playgroud)
如您所见,nop指令前有三条call指令.
首先我考虑了Debug/Release构建,但这不是编译器生成的代码,我发出了原始的IL代码,并希望看到它原样.
所以我的问题是为什么nop当我没有发出任何指令时有三条指令?
har*_*old 13
ILGenerator不是很高级,如果你使用Emit(OpCode, Int32)重载,它将整个int32放在指令流中,无论操作码是什么Ldc_I4(实际上需要4个字节的立即数)或Ldc_I4_S(它没有).
所以一定要使用正确的重载:
ilGenerator.Emit(OpCodes.Ldc_I4_S, (byte)120);
Run Code Online (Sandbox Code Playgroud)
在参考源中,Emit使用int参数执行此操作:
public virtual void Emit(OpCode opcode, int arg)
{
// Puts opcode onto the stream of instructions followed by arg
EnsureCapacity(7);
InternalEmit(opcode);
PutInteger4(arg);
}
Run Code Online (Sandbox Code Playgroud)
其中PutInteger4将四个字节写入构建IL的字节数组.
文档Emit说明额外的字节将是Nop指令,但只有当它们实际上为零时才是.如果传递的值是"更多错误"(高字节与零不同)那么效果可能会更糟,从无效的操作码到巧妙地破坏结果的操作.