【问题标题】:A general delegate type for handling any event用于处理任何事件的通用委托类型
【发布时间】:2009-10-15 04:47:12
【问题描述】:

我有一个函数,它接收两个参数——一个对象和一个定义该对象上的事件的 EventInfo 结构。我需要阻止该功能,直到指定的事件触发。我遇到的问题是,当处理程序的类型可以是任何东西时,如何将委托添加到指定的事件?请注意,我不关心结果事件调用的参数,我只需要了解它被引发的事实。

我已经尝试使用 EventInfo.AddEventHandler 添加一个非常通用的委托类型 (EventHandler),但无济于事。我也尝试过相同的方法,但使用Activator 创建EventInfo.EventHandlerType 属性中指定类型的实例,但没有乐趣。

或者,如果有人有办法做类似的事情,给定一个对象,以及该对象上的一个事件的名称,那么这也可以。

我正在使用 C# 和 .NET 2.0。

干杯

【问题讨论】:

    标签: c# .net events delegates


    【解决方案1】:

    解决方案的提示可以使用MethodBuilder 类。使用它,您可以在运行时生成适合 EventInfo 期望的委托的方法。

    基于它的示例(可以进行许多优化,但适用于大多数情况):

    namespace AutoEventListener
    {
        using System;
        using System.Linq;
        using System.Collections.Generic;
        using System.Reflection;
        using System.Reflection.Emit;
    
        public class EventExample
        {
            public static event EventHandler MyEvent;
    
            public void Test()
            {
                bool called;
                var eventInfo = GetType().GetEvent("MyEvent");
                EventFireNotifier.GenerateHandlerNorifier(eventInfo,
                    callbackEventInfo =>
                        {
                            called = true;
                        });
    
                MyEvent(null, null);;
            }
        }
    
        public class EventFireNotifier
        {
            static private readonly Dictionary<int, EventInfo> eventsMap = new Dictionary<int, EventInfo>();
            static private readonly Dictionary<int, Action<EventInfo>> actionsMap = new Dictionary<int, Action<EventInfo>>();
            static private int lastIndexUsed;
            public static MethodInfo GenerateHandlerNorifier(EventInfo eventInfo, Action<EventInfo> action)
            {
                MethodInfo method = eventInfo.EventHandlerType.GetMethod("Invoke");
                AppDomain myDomain = AppDomain.CurrentDomain;
                var asmName = new AssemblyName(){Name = "HandlersDynamicAssembly"};
    
                AssemblyBuilder myAsmBuilder = myDomain.DefineDynamicAssembly(
                    asmName,
                    AssemblyBuilderAccess.RunAndSave);
    
                ModuleBuilder myModule = myAsmBuilder.DefineDynamicModule("DynamicHandlersModule");
    
                TypeBuilder typeBuilder = myModule.DefineType("EventHandlersContainer", TypeAttributes.Public);
    
                var eventIndex = ++lastIndexUsed;
                eventsMap.Add(eventIndex, eventInfo);
                actionsMap.Add(eventIndex, action);
    
                var handlerName = "HandlerNotifierMethod" + eventIndex;
    
                var parameterTypes = method.GetParameters().Select(info => info.ParameterType).ToArray();
                AddMethodDynamically(typeBuilder, handlerName, parameterTypes, method.ReturnType, eventIndex);
    
                Type type = typeBuilder.CreateType();
    
                MethodInfo notifier = type.GetMethod(handlerName);
    
                var handlerDelegate = Delegate.CreateDelegate(eventInfo.EventHandlerType, notifier);
    
                eventInfo.AddEventHandler(null, handlerDelegate);
                return notifier;
            }
    
            public static void AddMethodDynamically(TypeBuilder myTypeBld, string mthdName, Type[] mthdParams, Type returnType, int eventIndex)
            {
                MethodBuilder myMthdBld = myTypeBld.DefineMethod(
                                                     mthdName,
                                                     MethodAttributes.Public |
                                                     MethodAttributes.Static,
                                                     returnType,
                                                     mthdParams);
    
                ILGenerator generator = myMthdBld.GetILGenerator();
    
                generator.Emit(OpCodes.Ldc_I4, eventIndex);
                generator.EmitCall(OpCodes.Call, typeof(EventFireNotifier).GetMethod("Notifier"), null);
                generator.Emit(OpCodes.Ret);
            }
    
            public static void Notifier(int eventIndex)
            {
                var eventInfo = eventsMap[eventIndex];
                actionsMap[eventIndex].DynamicInvoke(eventInfo);
            }
        }
    }
    

    EventFireNotifier 类为 EventInfo 注册一个 Action,当事件被触发时调用。

    希望对你有帮助。

    【讨论】:

    • 该方法似乎为您创建的每个方法都创建了一个 dll,这不会减慢它的速度吗?
    • 我认为缓慢的部分是创建方法(使用反射)但是一旦它被编译我认为它应该有很好的性能(但我从来没有这样做过所以我不能确定......) .
    • 这看起来确实是不错的代码(即使它使用 var,但我不能),但是我找到了一个更简单的解决方法,即要求顶层初始化代码为每个事件提供委托处理程序类型。
    【解决方案2】:

    我不完全理解这个问题,也许代码示例可能会更好。但是你可以做的是添加一个无参数委托作为事件处理程序。无论定义的事件的委托类型如何,这都将起作用。

    someobject.someevent += delegate{ // do whatever;} 
    

    【讨论】:

    • 为建议喝彩,但恐怕我实际上无法访问该事件,只能访问描述它的 EventInfo 结构。
    猜你喜欢
    • 1970-01-01
    • 2019-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多