2009-10-14 12 views
5

Mam funkcję, która odbiera dwa parametry - obiekt i strukturę EventInfo definiującą zdarzenie na tym obiekcie. Muszę zablokować tę funkcję do momentu uruchomienia określonego zdarzenia. Mam problem, w jaki sposób dodać delegata do określonego zdarzenia, gdy typem obsługi może być cokolwiek? Zauważ, że nie zależy mi na parametrach tego wynikowego wywołania zdarzenia, po prostu muszę złapać fakt, że jest on wywoływany.Typ delegata ogólnego do obsługi dowolnego zdarzenia

Próbowałem już używać EventInfo.AddEventHandler do dodania naprawdę ogólnego typu delegata (EventHandler), ale bez skutku. Próbowałem również tego samego, ale używając Activator, aby utworzyć instancję typu określonego we właściwości EventInfo.EventHandlerType, ale bez radości.

Ewentualnie, jeśli ktokolwiek ma sposób na wykonanie podobnego, podanego obiektu i nazwy zdarzenia na tym obiekcie, to też by działało.

Używam C# i .NET 2.0.

Cheers

Odpowiedz

2

Wskazówką na rozwiązanie może być klasa MethodBuilder. Za jego pomocą można wygenerować metodę w czasie wykonywania, która pasuje do delegata, którego oczekuje EventInfo.

Przykład opiera się na nim (Wiele optymalizacje można zrobić, ale to działa w większości przypadków):

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

Klasa EventFireNotifier rejestr dla EventInfodziałania która jest wywoływana, gdy zdarzenie jest zwolniony.

Mam nadzieję, że to pomaga.

+0

Ta metoda wydaje się tworzyć bibliotekę dll dla każdej tworzonej metody, czy nie spowolniłoby to nieco? – Kazar

+0

Myślę, że powolną częścią jest tworzenie metody (używa refleksji), ale po jej skompilowaniu myślę, że powinna mieć świetną wydajność (ale nigdy tego nie zrobiłem, więc nie mogę powiedzieć na pewno ...). – Elisha

+0

To wygląda jak dobry kod (nawet jeśli używa var, którego nie mogę), jednak znalazłem prostsze obejście mojego problemu, wymagając kodu inicjalizacyjnego najwyższego poziomu, aby zapewnić delegatów dla każdego typu procedury obsługi zdarzenia. – Kazar

0

ja nie do końca rozumiem pytanie, może przykładowy kod może być lepiej. Ale myślę, że możesz zrobić, to dodać delegata bez parametrów jako moduł obsługi zdarzenia. To zadziała niezależnie od typu delegata dla zdefiniowanego zdarzenia.

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

Pozdrawiam za sugestię, ale obawiam się, że tak naprawdę nie mam dostępu do wydarzenia, tylko do opisującej go struktury EventInfo. – Kazar

Powiązane problemy