如何让事件处理程序委托知晓调用它的事件名称
问题:在通用事件处理程序中获取触发事件的名称
我的代码提炼出了核心问题(来自一个更大的单元测试项目):
// 为myInstance中的所有事件定义所需的委托 EventHandler oea = (o, ea) => GenericEventHandler(o, ea); EventHandler<int> oi = (o, i) => GenericEventHandler(o, i); // 更多委托定义 myInstance.GetType().GetEvents().Foreach(ei => { // 此处检查EventInfo的委托以确定使用哪个处理程序 .... AssignEvent(myItem, ei, oea); // 或者 AssignEvent(myItem, ei, oi); // 或其他委托 .... }); ... private static void AssignEvent(object instance, EventInfo @event, Delegate handler) { var iName = instance.GetType().Name; WriteTrace($"Assign {@event.EventHandlerType.Name} to {iName}.{@event.Name}"); @event.AddEventHandler(instance, handler); } private static void GenericEventHandler(params object[] o) { WriteTrace("$Event from {....} invoked"); }
我希望在运行时知晓调用该处理程序的事件名称。@event.Name就在那里,但如何让处理程序获取到它呢?
由于事件处理程序是通过这种方式绑定的,GenericEventHandler内部的StackTrace无法提供事件名称的线索。
我已经尝试过DynamicMethod、反射、表达式等多种方式,但都未能找到可行方案。
我希望避免使用Emit和IL技术。
解决方案:通过闭包+表达式树传递事件名称
核心思路是在绑定每个事件时,将event.Name通过闭包嵌入到事件处理程序中,同时用表达式树适配不同类型的事件委托,无需手动定义每种委托的重载。
步骤1:修改通用处理方法,增加事件名称参数
private static void GenericEventHandler(string eventName, params object[] args) { var sender = args[0]; WriteTrace($"Event from {eventName} invoked (Sender: {sender})"); }
步骤2:编写动态创建带事件名称的委托的辅助方法
private static Delegate CreateHandlerWithEventName(string eventName, Type handlerType) { // 获取委托的Invoke方法 var invokeMethod = handlerType.GetMethod("Invoke") ?? throw new ArgumentException("Invalid delegate type"); // 构建委托的参数表达式 var paramExpressions = invokeMethod.GetParameters() .Select(p => Expression.Parameter(p.ParameterType)) .ToArray(); // 构建调用GenericEventHandler的表达式:传入事件名称+所有参数的object数组 var eventNameExpr = Expression.Constant(eventName); var argsArrayExpr = Expression.NewArrayInit( typeof(object), paramExpressions.Select(p => Expression.Convert(p, typeof(object))) ); var callExpr = Expression.Call( typeof(YourClassName), // 替换成包含GenericEventHandler的类名 nameof(GenericEventHandler), null, eventNameExpr, argsArrayExpr ); // 编译为目标类型的委托 var lambda = Expression.Lambda(handlerType, callExpr, paramExpressions); return lambda.Compile(); }
步骤3:修改事件绑定逻辑
// 替换原来的委托定义和遍历逻辑 myInstance.GetType().GetEvents().ToList().ForEach(ei => { var handler = CreateHandlerWithEventName(ei.Name, ei.EventHandlerType); AssignEvent(myInstance, ei, handler); });
原理说明
- 每个事件绑定的委托都会通过闭包捕获当前事件的
ei.Name,当事件触发时,该名称会被自动传入GenericEventHandler。 - 表达式树自动适配不同的事件委托类型(如
EventHandler、EventHandler<int>等),无需手动编写每种委托的匿名方法。 - 完全避免了Emit/IL操作,仅使用表达式树实现动态委托创建。
内容的提问来源于stack exchange,提问作者ThinkAgain
相关产品推荐
相关产品推荐

