我正在尝试动态创建函数,当执行时,只需调用给定的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
的类型,您尝试调用方法,该方法需要在调用之前堆栈中 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);
如果您无法更改动态方法签名您可以从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);