用于处理任何事件的常规委托类型

时间:2009-10-14 08:49:20

标签: c# .net events delegates

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

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

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

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

干杯

2 个答案:

答案 0 :(得分:2)

解决方案的提示可以使用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 ,在事件被触发时调用。

我希望它有所帮助。

答案 1 :(得分:0)

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

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