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