C#/CIL/Reflection.发出问题:
我试图定义一个有函数指针的类型,实例化该类型,在另一个类型上创建一个静态方法(因为我不知道如何制作"只是一个函数"(,给实例一个指向该静态方法的指针,然后使用该指针调用函数。
我几乎没有成功-(
这是类型:
Thunk = modb.DefineType("Thunk");
Thunk.DefineField("Env" , Env.AsType(), FieldAttributes.Public);
Thunk.DefineField("Expr", typeof(int), FieldAttributes.Public); // int is the correct type according to http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.ldftn.aspx
Thunk.CreateType();
到目前为止还不错(我认为(。然后我创建一个这样的家伙,并分配函数指针:
var methodBuilder = MainType.DefineMethod("my_other_little_function", MethodAttributes.Static, typeof(Int64), new[] {Env.AsType()});
{
var il2 = methodBuilder.GetILGenerator();
il2.Emit(OpCodes.Ldarg_0);
il2.Emit(OpCodes.Stloc_0);
binding.Expr.Compile(il2);
il2.Emit(OpCodes.Ret);
}
il.Emit(OpCodes.Newobj, ThunkCtor);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Stfld, Thunk.GetField("Env"));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldftn, methodBuilder);
il.Emit(OpCodes.Stfld, Thunk.GetField("Expr"));
据我所知,这部分工作得还不错。问题出在其他地方,当我试图称之为时
var func = il.DeclareLocal(typeof(int));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldfld, Thunk.GetField("Expr"));
il.Emit(OpCodes.Stloc, func);
il.Emit(OpCodes.Ldfld, Thunk.GetField("Env"));
il.Emit(OpCodes.Ldloc, func);
il.EmitCalli(OpCodes.Calli, CallingConventions.Standard, typeof(Int64), new[] { Env.AsType() }, null);
如果我试图运行由此创建的程序,我会在执行任何字节码之前出错(或者至少它是这样出现的(:
Unhandled Exception: System.InvalidProgramException: Common Language Runtime detected an invalid program.
如果我将EmitCali((替换为简单地弹出函数指针和参数并推送一个数字的代码,那么程序的其余部分就可以正常工作。那么我该如何调用这个函数呢?
非常感谢。:-(
除非你只是为了好玩而喜欢解决这样的问题,否则另一种选择是使用库来生成所需的IL。
Fasterflect是一个包含大量反射辅助对象的库。例如,要获得方法的委托,只需写:
var delegate = typeof(YourClass).DelegateForCallStaticMethod( "MyStaticMethod" );
delegate( args );
该库在后台使用DynamicMethod和IL生成,还具有不需要为委托声明和使用变量的扩展(尽管如果您打算重复调用它,例如在紧密循环中(。简单的变体如下:
var result = typeof(YourClass).CallStaticMethod( "MyStaticMethod", args );
Fasterflect缓存生成的委托(因为编译生成的IL非常昂贵(,因此在简单场景中,每次调用所增加的性能成本相当于进行缓存查找的成本。
免责声明:我参与了上述项目。也就是说,手写IL真的不是一项有趣的任务。IL一代的另一个好选择是Mono.Cecil库,尽管我不熟悉它提供的细节。
IntPtr
,而不是int
。CLR文档有时将这种类型称为"原生int"。