2010-09-23 19 views
7

Necesito obtener todos los eventos de la clase actual y conocer los métodos que se suscriben. Here I got some answers on how to do that, pero no sé cómo puedo obtener el delegate cuando todo lo que tengo es EventInfo.¿Cómo obtener un objeto delegado de un EventInfo?

var events = GetType().GetEvents(); 

foreach (var e in events) 
{ 
    Delegate d = e./*GetDelegateFromThisEventInfo()*/; 
    var methods = d.GetInvocationList(); 
} 

¿Es posible obtener un delegado con el EventInfo? ¿Cómo?

+1

Citando de la respuesta de la más alta votación en su pregunta anterior: "Ahora supongo que se podría tratar de encontrar el cuerpo del '' manejador añadir, descompilarlo y averiguar cómo están siendo los controladores de eventos almacenados, y tráigalos de esa manera ... ** pero no lo hagas **. Estás creando mucho trabajo, solo para romper la encapsulación. Solo ** rediseña tu código ** para que no necesites hacer esto." De todo corazón estoy de acuerdo. –

Respuesta

12

La declaración var events = GetType().GetEvents(); le ofrece una lista de los objetos EventInfo asociados con el tipo actual, no la instancia actual per se. Por lo tanto, el objeto EventInfo no contiene información sobre la instancia actual y, por lo tanto, no conoce a los delegados cableados.

Para obtener la información que desea, necesita obtener el campo de respaldo para el controlador de eventos en su instancia actual. Así es como:

public class MyClass 
{ 
    public event EventHandler MyEvent; 

    public IEnumerable<MethodInfo> GetSubscribedMethods() 
    { 
     Func<EventInfo, FieldInfo> ei2fi = 
      ei => this.GetType().GetField(ei.Name, 
       BindingFlags.NonPublic | 
       BindingFlags.Instance | 
       BindingFlags.GetField); 

     return from eventInfo in this.GetType().GetEvents() 
       let eventFieldInfo = ei2fi(eventInfo) 
       let eventFieldValue = 
        (System.Delegate)eventFieldInfo.GetValue(this) 
       from subscribedDelegate in eventFieldValue.GetInvocationList() 
       select subscribedDelegate.Method; 
    } 
} 

Así que ahora su código de llamada puede tener este aspecto:

class GetSubscribedMethodsExample 
{ 
    public static void Execute() 
    { 
     var instance = new MyClass(); 
     instance.MyEvent += new EventHandler(MyHandler); 
     instance.MyEvent += (s, e) => { }; 

     instance.GetSubscribedMethods() 
      .Run(h => Console.WriteLine(h.Name)); 
    } 

    static void MyHandler(object sender, EventArgs e) 
    { 
     throw new NotImplementedException(); 
    } 
} 

El resultado de lo anterior es:

MyHandler 
<Execute>b__0 

Estoy seguro de que puede jig alrededor con el código si desea devolver el delegado en lugar de la información del método, etc.

Espero que esto ayude.

+0

Esto es realmente críptico, ni siquiera puedo leerlo. – jcmcbeth

+0

@jcmcbeth - ¿Qué parte es críptica? – Enigmativity

+1

Your EventInfo to FieldInfo delegate (ei2fi) no parece funcionar para [eventos con un agregado y una eliminación personalizados] (http://msdn.microsoft.com/en-us/magazine/cc163533.aspx). –

2

forma similar a Enigmativity, la lista de invocación se puede encontrar en otras categorías, no sólo la clase actual ...

private void testit() 
    { 
     WithEvents we = new WithEvents(); 
     we.myEvent += new EventHandler(we_myEvent); 
     we.myEvent += new EventHandler(we_myEvent2); 

     foreach (EventInfo ev in we.GetType().GetEvents()) 
     { 
      FieldInfo fi = we.GetType().GetField(ev.Name, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy); 
      Delegate del = (Delegate)fi.GetValue(we); 
      var list = del.GetInvocationList(); 
      foreach (var d in list) 
      { 
       Console.WriteLine("{0}", d.Method.Name); 
      } 
     } 
    } 

    void we_myEvent(object sender, EventArgs e) 
    { 
    } 
    void we_myEvent2(object sender, EventArgs e) 
    { 
    } 


public class WithEvents 
{ 
    public event EventHandler myEvent; 
} 

... siempre y cuando los controladores de eventos se declaran en la clase como vemos arriba Pero considere la clase Control donde se almacena la Lista de eventos en la propiedad "Eventos" y cada nombre de campo de evento comienza con "Evento" seguido del nombre del evento. Luego están las clases derivadas del Formulario que parecen administrar eventos de manera diferente. Comida para el pensamiento.

2

Para mi caso, el valor del campo (clase ToolStripMenuItem, campo EventClick) es frustrantemente de tipo objeto, no delegado. Tuve que recurrir a la propiedad Events que Les mencionó en su respuesta, de una manera que recibí de here. El campo EventClick en este caso solo contiene la clave de EventHandlerList almacenada en esta propiedad.

Algunas otras observaciones:

  • La regla fieldName = "Event" + eventName I empleado no va a funcionar en todos los casos. Lamentablemente, no existe una regla común para vincular el nombre del evento con el nombre del campo. Puede intentar usar un diccionario estático para la asignación, similar a this article.
  • Nunca estoy seguro del BindingFlags. En otro escenario, puede que tenga que ajustarlos.

    /// <summary> 
    /// Gets the EventHandler delegate attached to the specified event and object 
    /// </summary> 
    /// <param name="obj">object that contains the event</param> 
    /// <param name="eventName">name of the event, e.g. "Click"</param> 
    public static Delegate GetEventHandler(object obj, string eventName) 
    { 
        Delegate retDelegate = null; 
        FieldInfo fi = obj.GetType().GetField("Event" + eventName, 
                  BindingFlags.NonPublic | 
                  BindingFlags.Static | 
                  BindingFlags.Instance | 
                  BindingFlags.FlattenHierarchy | 
                  BindingFlags.IgnoreCase); 
        if (fi != null) 
        { 
         object value = fi.GetValue(obj); 
         if (value is Delegate) 
          retDelegate = (Delegate)value; 
         else if (value != null) // value may be just object 
         { 
          PropertyInfo pi = obj.GetType().GetProperty("Events", 
                  BindingFlags.NonPublic | 
                  BindingFlags.Instance); 
          if (pi != null) 
          { 
           EventHandlerList eventHandlers = pi.GetValue(obj) as EventHandlerList; 
           if (eventHandlers != null) 
           { 
            retDelegate = eventHandlers[value]; 
           } 
          } 
         } 
        } 
        return retDelegate; 
    } 
    
+0

Me ayudó muchísimo. ¡Gracias! – Law

Cuestiones relacionadas