为C#中的所有事件和委托创建一个catch-all处理程序
我想创建一个可用于处理任何事件或委托的处理程序。 具体来说,我希望能够编写如下代码:
class Invoker { public object Invoke(object[] arg) { // generic handling code } } static void Main() { var p = new Person(); p.AddHandler("Event1", new Invoker().Invoke); }
AddHandler
是object
的扩展方法,它接收事件名称和类型为Func
的委托。 它应该能够做任何魔术来将事件(例如,在这种情况下为Event1
)绑定到提供的委托,以便在触发事件时调用委托。
Event1
的签名无关紧要,因为AddHandler
应该适用于所有类型的事件(和委托)。
我怀疑这可能涉及一些CIL生成来构建匹配指定事件类型的动态委托(例如Event1
)并将调用转发给指定的委托(例如new Invoker().Invoke
)。 我能够构建这样一个动态委托,但它只能转发到静态方法,而不是实例方法,因为我找不到将待调用方法的绑定实例推送到CLR堆栈的方法(即示例中的Invoker
实例)。 请参阅下面提供的代码以清楚地查看此问题(请参阅标有ISSUE的行)。
如果有人能指出一种方法来改进动态生成代码以捕获绑定对象或更好,建议一个更简单的解决方案,不需要CIL然后非常感激。
public static void AddHandler(this object target, string fieldName, Func func) { var eventInfo = target.GetType().GetEvent(fieldName); if (eventInfo != null) { Type delegateType = eventInfo.EventHandlerType; var dynamicHandler = BuildDynamicHandler(target.GetType(), delegateType, func); eventInfo.GetAddMethod().Invoke(target, new Object[] { dynamicHandler }); } } public static Delegate BuildDynamicHandler(this Type delegateOwnerType, Type delegateType, Func func) { MethodInfo invokeMethod = delegateType.GetMethod("Invoke"); Type returnType = invokeMethod.ReturnType; bool hasReturnType = returnType != Constants.VoidType; var paramTypes = invokeMethod.GetParameters().Select(p => p.ParameterType).ToArray(); var dynamicMethod = new DynamicMethod("add_handler", hasReturnType ? returnType : null, paramTypes, delegateOwnerType); var il = new EmitHelper(dynamicMethod.GetILGenerator()); if (paramTypes.Length == 0) { il.ldnull.end(); } else { il.DeclareLocal(typeof(object[])); il.ldc_i4(paramTypes.Length); il.newarr(typeof(object)); il.stloc_0.end(); for (int i = 0; i < paramTypes.Length; i++) { il.ldloc_0 .ldc_i4(i) .ldarg(i) .boxIfValueType(paramTypes[i]) .stelem_ref.end(); } il.ldloc_0.end(); } /////// ****************** ISSUE: work for static method only il.call(func.Method); if (hasReturnType) { il.unbox_any(returnType).ret(); } else { il.pop.ret(); } return dynamicMethod.CreateDelegate(delegateType); }
这是使用表达式树的实现:
public static Delegate BuildDynamicHandle(Type delegateType, Func
你看过使用表达式树( http://msdn.microsoft.com/en-us/library/bb397951.aspx )吗? 它们使生成IL更容易。
我找到了一个解决方案。 我在这里用完整的代码写了博客,如果有人对纯CIL生成方法感兴趣(这不像kvb的方法那么优雅)。