我正在写一个框架,其中一个函数涉及远程处理事件(我认为现在不相关的细节)。但是需要的是,在给定目标对象引用和事件名称的情况下,可以将事件处理程序挂接到任何事件。添加处理程序的任何事件与反射,动态处理
处理事件的方法会编组/否则将参数代理到某些处理程序代码,然后将返回类型转换为事件处理程序的返回类型。有效地,它包装处理程序。
我相信,由于事件处理程序可能需要有任何类型的任何数量的参数,处理挂钩事件按需的请求的唯一方法是使用反射/发射(代码代)构建自定义代理方法。我发布这个问题来检查这个假设,看看有没有人有任何其他/整洁的想法。
目前,我创建了一个DynamicMethod
,然后做这种事情。它几乎可行 - 但是这种动态代理方法调用的包装器方法必须是静态的(声明几乎是public static object GenericHandler(object[] parameters)
);我不想要它。所以我目前的任务是动态地创建一个完整的类型,使用这个方法,但也是一个字段来存储对象的实例来调用包装器。但是,这并不重要(我认为) - 我想检查一下,关于必须创建动态代码的假设是否正确。但是,对于裁判:
// obj = target object, eventName = event name
var evtInfo = obj.GetType().GetEvent(eventName);
var handlerType = evtInfo.EventHandlerType;
var eventInvokeMethod = handlerType.GetMethod("Invoke");
var paramTypes = eventInvokeMethod.GetParameters().Select(p => p.ParameterType).ToArray();
var returnType = eventInvokeMethod.ReturnType;
var handlerMethod = new DynamicMethod(
"evtHandler_" + eventName,
MethodAttributes.Static | MethodAttributes.Public,
CallingConventions.Standard,
returnType,
paramTypes,
typeof(my container class).Module,
false);
var il = handlerMethod.GetILGenerator();
// locals: [0] = parameter array
il.DeclareLocal(typeof(object[]));
// create an appropriately-sized array objects, for the parameters
il.Emit(OpCodes.Ldc_I4, (int)paramTypes.Count());
il.Emit(OpCodes.Newarr, typeof(object));
il.Emit(OpCodes.Stloc_0);
for (int i = 0; i < paramTypes.Length; i++)
{
// for the array: load the parameter into the same index of the array
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldc_I4, i);
il.Emit(OpCodes.Ldarg, i);
if (!paramTypes[i].IsClass)
{
// box the value first..
il.Emit(OpCodes.Box, paramTypes[i]);
}
il.Emit(OpCodes.Stelem_Ref);
}
// call our generic handler with the array of parameters, then convert the return
// value to the target type via unboxing or a cast
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Call, wrapper.GetMethodInfo());
if (!returnType.IsClass)
{
il.Emit(OpCodes.Unbox_Any, returnType);
}
else
{
il.Emit(OpCodes.Castclass, returnType);
}
il.Emit(OpCodes.Ret);
如果没有反应,我与包装类型的工作上面的理论,我会发布更新的代码..但很想听到替代这种方法,如果他们都出来了那里。
此外,一般评论:'发射'是一个巨大的痛苦与合作。我没有提供一个参数给'InvalidProgramException',CLR运行时错误和其他同样没有问题的塞子之间的'Box'操作码,它花了一个多小时才发现这是导致我的问题.. –
还有,一般空闲认为:也许我们所看到的所有'标准'事件处理程序都使用相同的兼容处理程序签名'(object,EventArgs)',因为上述过程非常痛苦? –