用Emit动态调用Func <t,object [],=“”object =“”>

时间:2017-03-05 19:12:36

标签: c# system.reflection reflection.emit

我正在尝试动态创建函数,在执行时只调用给定的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;

    }

我正在使用反射创建一个动态类型,并且每次调用此Override方法时,我需要在此Dynamic创建的对象中创建给定的方法(覆盖现有的方法,即ToString())。

我已经尝试过各种方式使用Emit和EmitCall,但我所得到的只是InvalidProgramException或者根本没有。

我想要实现的目标是:

  • 对于给定的Func,要覆盖一个方法,当它被调用时,会触发此Func,并返回其结果,所有这些都使用ILGenerator。我怎样才能做到这一点?我已经被困了好几天没有任何作用。

1 个答案:

答案 0 :(得分:2)

第一个问题是当你在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);

如果您 无法更改动态方法签名 ,则可以从T参数中删除Func并将其作为另一个参数路径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);