c# - 使用 Emit 动态调用 Func<T, object[], object>

标签 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 方法时,我都需要在此动态创建的对象中创建给定的方法(覆盖先前存在的方法,即 ToString() )。

我已经尝试过使用 Emit 和 EmitCall 的各种方式,但我得到的要么是 InvalidProgramException,要么什么也没有。

我想要实现的是:

  • 对于给定的 Func,要重写一个方法,当调用该方法时,会触发此 Func 并返回其结果,所有这些都使用 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输入您尝试调用需要 T 类型参数的方法和object[]在调用它之前在堆栈中。目前您只加载 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);

关于c# - 使用 Emit 动态调用 Func<T, object[], object>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42612767/

相关文章:

c# - .NET:为什么嵌套类具有在外部类中声明的泛型?

c# - 了解 ASP.net 跟踪

c# - OpenGL 可编程管道运行速度较慢?

c# - 获取最近的时间

c# - 在对象初始化之前使用反射设置静态变量值?

.net - 修改现有的 .NET 程序集

c# - 从动态程序集/模块中删除类

c# - GetProperty 反射在新属性上产生 "Ambiguous match found"

c# - OpCode.Call to Generic Method in different assembly 使用原始 IL