2010-09-24 19 views
5

Recibo todos los eventos, con cierto atributo, y deseo modificar estos eventos agregando una llamada a otro método.¿Cómo puedo cambiar un método asignado en un evento?

var type = GetType(); 
var events = type.GetEvents().Where(e => e.GetCustomAttributes(typeof(ExecuteAttribute), false).Length > 0); 

foreach (var e in events) 
{ 
    var fi = type.GetField(e.Name, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField); 
    var d = (Delegate)fi.GetValue(this); 
    var methods = d.GetInvocationList(); 
    foreach (var m in methods) 
    { 
     var args = e.EventHandlerType.GetMethod("Invoke").GetParameters().Select(p => Expression.Parameter(p.ParameterType, "p")).ToArray(); 
     var body = m.Method.GetMethodBody(); 

     /** 

     TODO: 

     Create a new method with the body of the previous 
     and add a call to another method 

     Remove current method 
     Add the new created method 

     **/ 
    } 
} 

Lo que quiero es básicamente lo que se comentó anteriormente. "Modificar" los métodos suscritos del evento. Supongo que no me puedo suscribir porque necesito pasar los parámetros que el método está pasando al controlador grande (el nuevo método).

Un ejemplo más basado en on this question. Quiero convertir esto:

var e += (x) => 
{ 
    var y = x; 
}; 

Para algo como esto:

var e += (x) => 
{ 
    var y = x; 

    BigHandler(x); // injected code 
}; 

O esto:

var e += (x) => // new method 
{ 
    previousE(x); // previous method 

    BigHandler(x); // additional code 
} 

¿Cómo puedo hacerlo?


El objetivo más grande:

que necesito para "detectar" cuando se dispara un evento y llamar a un método. También necesito enviar los parámetros que está usando.

que yo pueda hacer algo como:

public delegate void OnPostSaved(Post p); 
[Execute] 
public event OnPostSaved PostSaved; 

public void Save() 
{ 
    /* save stuff */ 
    // assume that there is already an event subscribed 
    PostSaved(post); 
} 

Entonces en mi método de control que se puede comprobar si se ha disparado un evento de XYZ, comprobar qué evento fue despedido, recuperar el parámetro y hacer algo. Por ejemplo:

public void BigHandler(string eventName, params object[] p) 
{ 
    if (eventName == "PostSaved") 
    { 
     var post = p[0] as Post; 
     MessageBoard.Save("User posted on the blog: " + post.Content); 
    } 
} 

Sé que se puede lograr utilizando PostSharp, pero no puedo usarlo. Necesito otra solución.


relacionados


actualización 2010-09-27 no pude encontrar una solución ni más información sobre es, todavía necesito ayuda. Se agregó +150 recompensa.

Respuesta

4

Si solo tienes sumando una llamada, es muy fácil: no necesitas buscar a todos los delegados suscritos ni nada por el estilo; Solo necesita suscribir con la reflexión:

var type = GetType(); 
// Note the small change here to make the filter slightly simpler 
var events = type.GetEvents() 
       .Where(e => e.IsDefined(typeof(ExecuteAttribute), false)); 

foreach (var e in events) 
{ 
    // "handler" is the event handler you want to add 
    e.AddEventHandler(this, handler); 
} 

Ahora, he hecho una suposición sobre lo que quiere hacer - es decir, llamar a su método una vez cada vez que el evento se produce. Eso no es lo mismo que llamar a su método una vez cada vez que se llame a un controlador de eventos, que es lo que habría hecho su código original. Eso es mucho más difícil ...pero, ¿realmente lo necesitas?

(si se puede dar más información acerca de la meta más grande aquí, sería de gran ayuda.)

Tenga en cuenta que el código que he proporcionado aquí debería funcionar independientemente de cómo se implementa el evento - que doesn Confiar en que sea un evento de campo.

EDIT: Bueno, aquí hay un ejemplo completo que muestra cómo puede agregar una llanura EventHandler delegado ante un evento que coincide con el patrón de concurso completo la normalidad:

using System; 
using System.Reflection; 

class CustomEventArgs : EventArgs {} 

delegate void CustomEventHandler(object sender, CustomEventArgs e); 

class Publisher 
{ 
    public event EventHandler PlainEvent; 
    public event EventHandler<CustomEventArgs> GenericEvent; 
    public event CustomEventHandler CustomEvent; 

    public void RaiseEvents() 
    { 
     PlainEvent(this, new EventArgs()); 
     GenericEvent(this, new CustomEventArgs()); 
     CustomEvent(this, new CustomEventArgs()); 
    } 
} 

class Test 
{ 
    static void Main() 
    { 
     Publisher p = new Publisher(); 

     Type type = typeof(Publisher); 

     foreach (EventInfo eventInfo in type.GetEvents()) 
     { 
      string name = eventInfo.Name; 
      EventHandler handler = (s, args) => ReportEvent(name, s, args); 
      // Make a delegate of exactly the right type 
      Delegate realHandler = Delegate.CreateDelegate(
       eventInfo.EventHandlerType, handler.Target, handler.Method); 
      eventInfo.AddEventHandler(p, realHandler); 
     } 

     p.RaiseEvents(); 
    } 

    static void ReportEvent(string name, object sender, EventArgs args) 
    { 
     Console.WriteLine("Event {0} name raised with args type {1}", 
          name, args.GetType()); 
    } 
} 

Nota cómo tenemos que construir el tipo exacto de delegado necesaria - pero sabemos que podemos hacerlo desde un compatible con el método, siempre que todo siga el patrón de evento normal.

+0

Agregué el "objetivo más grande". Tu respuesta actual está cerca, siento haber olvidado mencionar que necesito obtener los parámetros que está pasando el método de suscripción. He agregado más información ... – BrunoLM

+0

@ BrunoLM: Eso es solo cuestión de crear el delegado apropiado, por su aspecto. Voy a salir ahora, pero echaré un vistazo en aproximadamente una hora, si aún no lo has resuelto. Las expresiones Lambda bien pueden ser tu amigo. –

+0

@Jon Skeet: Todavía no lo entiendo ... ¿Cómo puedo obtener el objeto que está usando el evento? La única forma en que puedo pensar es modificar el mismo método. Necesito algo como 'BigHandler (eventName, params_from_the_event.ToArray ()' – BrunoLM

Cuestiones relacionadas