我正在尝试动态创建函数,在执行时只调用给定的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或者根本没有。
我想要实现的目标是:
答案 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
类型,您尝试在调用之前调用堆栈中需要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);