用 Emit 动态调用 Func<T, object[], object>
Dynamically call Func<T, object[], object> with 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);
如果您无法更改动态方法签名,您可以从 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);
我正在尝试动态创建函数,在执行时只调用给定的 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);
如果您无法更改动态方法签名,您可以从 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);