2009-10-14 17 views
5

Tengo una función que recibe dos parámetros: un objeto y una estructura EventInfo que define un evento en ese objeto. Necesito bloquear esa función hasta que se active el evento especificado. El problema que tengo es, ¿cómo agrego un delegado al evento especificado, cuando el tipo de controlador puede ser cualquier cosa? Tenga en cuenta que no me importan los parámetros de esa llamada de evento resultante, solo necesito captar el hecho de que se plantea.Un tipo de delegado general para manejar cualquier evento

Ya he intentado usar EventInfo.AddEventHandler para agregar un tipo de delegado realmente general (EventHandler), pero fue en vano. También he intentado lo mismo, pero usando Activator para crear una instancia del tipo especificado en la propiedad EventInfo.EventHandlerType, pero no me gusta.

Alternativamente, si alguien tiene una manera de hacer algo similar, dado un objeto y el nombre de un evento en ese objeto, entonces eso también funcionaría.

Estoy usando C# y .NET 2.0.

Saludos

Respuesta

2

Una pista para la solución se puede hacer con la clase MethodBuilder. Utilizándolo, puede generar un método en tiempo de ejecución que se ajuste al delegado que espera EventInfo.

Ejemplo basado en él (Muchas optimizaciones se puede hacer, pero funciona para la mayoría de los casos):

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); 
     } 
    } 
} 

La clase EventFireNotifier registro de EventInfo un Acción que se llama cuando el evento es despedido.

Espero que ayude.

+0

El método parece crear un archivo DLL para cada método que crees, ¿no podría eso ralentizarlo un poco? – Kazar

+0

Creo que la parte lenta es crear el método (usa reflexión) pero una vez compilado creo que debería tener un buen rendimiento (pero nunca lo hice, así que no puedo asegurarlo ...). – Elisha

+0

Parece un buen código (aunque utiliza var, lo que no puedo), sin embargo, encontré una solución más simple para mi problema, requiriendo que el código de inicialización de nivel superior proporcione delegados para cada tipo de controlador de eventos. – Kazar

0

que no entiendo totalmente la cuestión, tal vez un ejemplo de código podría haber sido mejor. Pero uno cree que puede hacer es agregar un delegado sin parámetros como el manejador de eventos. Esto funcionará independientemente del tipo de delegado para el evento definido.

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

Saludos por la sugerencia, pero me temo que no tengo acceso al evento, solo a la estructura de EventInfo que lo describe. – Kazar

Cuestiones relacionadas