使用 Emit 动态调用 Func<T、object[]、object>



我正在尝试动态创建函数,当执行时,只需调用给定的func

 public IProxifier<T> Override(string method, Func<T, object[], object> handler)
    {
        if (!overr.ContainsKey(method))
        {
            Ops op = new Ops();
            op.GenericTypes = new Type[] { typeof(T) };
            op.MethodInfo = handler.GetMethodInfo();
            MethodBuilder mb = tb.DefineMethod(method, MethodAttributes.Public | MethodAttributes.ReuseSlot |
                    MethodAttributes.Virtual | MethodAttributes.HideBySig, typeof(object), new Type[] { typeof(object[]) });
            ILGenerator il = mb.GetILGenerator();

            //il.EmitCall(OpCodes.Callvirt, op.MethodInfo, op.GenericTypes);
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Callvirt, handler.GetMethodInfo());
            il.Emit(OpCodes.Ret);
            overr.Add(method, op);
        }
        return this;
    }

我正在使用反射创建动态类型,每次此覆盖方法都需要在此动态创建的对象中创建给定方法(覆盖预先存在的方法,即toString())。

我已经用Emit和EmitCall尝试了各种方式,但是我所获得的只是无效的Programexception,或者根本没有。

我要实现的目标是:

  • 对于给定的功能,要覆盖一种方法,当称为该弹药时,将触发此功能并返回其结果,所有这些都带有iLgenerator。我怎样才能做到这一点?我已经被困了几天了,没有任何可行。

第一个问题是,当您在IL中调用方法时,它总是以第一个参数对象为第一个参数对象。对于静态方法,此参数为null,但仍应存在。因此,第一个修复是加载null在其他参数之前堆叠:

il.Emit(OpCodes.Ldnull); // method object parameter
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Callvirt, handler.GetMethodInfo());
il.Emit(OpCodes.Ret);

第二个问题是根据handler的类型,您尝试调用方法,该方法需要在调用之前堆栈中 Tobject[]的参数。当前,您仅加载类型object[]的第二个参数,因此您也应使用类型T的参数。

根据您要做的事情,您可以以不同的方式解决它:

最简单的选项是将参数T添加到生成的方法:

MethodBuilder mb = tb.DefineMethod(method, MethodAttributes.Public | 
    MethodAttributes.ReuseSlot | MethodAttributes.Virtual | MethodAttributes.HideBySig, 
    typeof(object), new Type[] { typeof(T), typeof(object[]) });

然后使用它的值来调用Func

il.Emit(OpCodes.Ldnull);  // method object parameter
il.Emit(OpCodes.Ldarg_0); // first parameter of type T
il.Emit(OpCodes.Ldarg_1); // second parameter of type object[]
il.Emit(OpCodes.Callvirt, handler.GetMethodInfo());
il.Emit(OpCodes.Ret);

如果您无法更改动态方法签名您可以从Func参数删除T,然后将其作为Override方法的另一个参数将其作为另一个参数:

public IProxifier<T> Override(string method, T someName, Func<object[], object> handler)

如果您也无法更改Func的签名,但是您不使用 中的T值,则可以将默认值加载到堆栈中。通过加载null

可以轻松完成参考类型
il.Emit(OpCodes.Ldnull);  // method object parameter
il.Emit(OpCodes.Ldnull);  // first parameter of type T
il.Emit(OpCodes.Ldarg_1); // second parameter of type object[]
il.Emit(OpCodes.Callvirt, handler.GetMethodInfo());
il.Emit(OpCodes.Ret);

但是,对于价值类型,它变得更棘手,您应该使用此类型来声明本地变量,然后调用initobj指令以创建值,然后将其加载到堆栈中:

il.Emit(OpCodes.Ldloca_S, generator.DeclareLocal(typeof(T)); //create local variable
il.Emit(OpCodes.Initobj, typeof(T)); //initialize it with default value
il.Emit(OpCodes.Ldnull);  // method object parameter
il.Emit(OpCodes.Ldloc_0); // first parameter of type T from local variable
il.Emit(OpCodes.Ldarg_0); // second parameter of type object[] from argument
il.Emit(OpCodes.Call, handler.GetMethodInfo());
il.Emit(OpCodes.Ret);

相关内容

最新更新