2012-11-12 76 views
1

我正在写一个框架,其中一个函数涉及远程处理事件(我认为现在不相关的细节)。但是需要的是,在给定目标对象引用和事件名称的情况下,可以将事件处理程序挂接到任何事件。添加处理程序的任何事件与反射,动态处理

处理事件的方法会编组/否则将参数代理到某些处理程序代码,然后将返回类型转换为事件处理程序的返回类型。有效地,它包装处理程序。

我相信,由于事件处理程序可能需要有任何类型的任何数量的参数,处理挂钩事件按需的请求的唯一方法是使用反射/发射(代码代)构建自定义代理方法。我发布这个问题来检查这个假设,看看有没有人有任何其他/整洁的想法。

目前,我创建了一个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); 

如果没有反应,我与包装类型的工作上面的理论,我会发布更新的代码..但很想听到替代这种方法,如果他们都出来了那里。

+0

此外,一般评论:'发射'是一个巨大的痛苦与合作。我没有提供一个参数给'InvalidProgramException',CLR运行时错误和其他同样没有问题的塞子之间的'Box'操作码,它花了一个多小时才发现这是导致我的问题.. –

+0

还有,一般空闲认为:也许我们所看到的所有'标准'事件处理程序都使用相同的兼容处理程序签名'(object,EventArgs)',因为上述过程非常痛苦? –

回答

0

有必要实施(并改进)上述代码。通过动态生成的程序集,类型和方法,我可以将任何事件触发到一般的方法object HandleEvent(object[] parameters)

相关问题