简体   繁体   中英

How can I change a method assigned on an event?

I am getting all events, with a certain attribute, and I want to modify these events adding a call to another method.

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

        **/
    }
}

What I want is basically what is commented above. "Modify" the subscribed methods of the event. I guess I can't subscribe to it, because I need to pass the parameters the method is passing to the big handler (the new method).

One more example based on this question . I want to convert this:

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

To something like this:

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

    BigHandler(x); // injected code
};

Or this:

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

    BigHandler(x); // additional code
}

How can I do it?


The bigger goal:

I need to "detect" when an event is fired and call a method. I also need to send the parameters it is using.

So I can make something like:

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

Then on my handler method I can check if an event from XYZ was fired, check which event was fired, retrieve the parameter and do something. For example:

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

I know it can be achieved using PostSharp , but I can't use it. I need another solution.


Related


Update 2010-09-27 I couldn't find a solution neither more info on it, I still need help. Added +150 bounty.

If you're just adding a call, it's really easy - you don't need to fetch all the subscribed delegates or anything like that; just subscribe with reflection:

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

Now, I've made an assumption about what you want to do - namely call your method once each time the event is raised. That's not the same as calling your method once every time an event handler is called which is what your original code would have done. That's much harder... but do you really need it?

(If you could give more information about the bigger goal here, it would really help.)

Note that the code I've provided here should work regardless of how the event is implemented - it doesn't rely on it being a field-like event.

EDIT: Okay, here's a complete example showing how you can add a plain EventHandler delegate to any event which matches the normal eventing pattern:

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

Note how we have to construct the exact type of delegate required - but we know we can do that from a compatible method, so long as everything follows the normal event pattern.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM