如何保存MethodInfo指针并稍后调用该函数?

时间:2012-12-22 20:41:01

标签: c# cil reflection.emit methodinfo

C#/ CIL / Reflection.Emit问题:

我正在尝试定义一个具有函数指针的类型,实例化该类型,在另一个类型上创建一个静态方法(因为我不知道如何使“只是一个函数”),给一个指针这个静态方法到实例,后来用那个指针来调用函数。

我收效甚微。 : - (

这是类型:

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.

如果我用代码替换EmitCalli()来简单地弹出函数指针和参数并推送一个数字,程序的其余部分工作正常。那我怎么称呼这个函数呢?

非常感谢。 : - )

2 个答案:

答案 0 :(得分:1)

除非你喜欢解决这样的问题只是为了它的乐趣,否则另一种选择是使用库来生成所需的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库,虽然我不熟悉它提供的细节。

答案 1 :(得分:0)

您应该将IntPtr用于函数指针而不是int。 CLR文档有时称为此类型" native int"。