Как я могу изменить метод, назначенный на событие? - PullRequest
5 голосов
/ 24 сентября 2010

Я получаю все события с определенным атрибутом, и я хочу изменить эти события, добавив вызов другому методу.

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

        **/
    }
}

То, что я хочу, - это в основном то, что прокомментировано выше.«Изменить» подписанные методы события.Я думаю, что не могу подписаться на него, потому что мне нужно передать параметры, которые метод передает большому обработчику (новый метод).

Еще один пример, основанный на этом вопросе ,Я хочу преобразовать это:

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

Во что-то вроде этого:

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

    BigHandler(x); // injected code
};

Или вот это:

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

    BigHandler(x); // additional code
}

Как я могу это сделать?


Большая цель:

Мне нужно «обнаружить», когда происходит событие, и вызвать метод.Мне также нужно отправить параметры, которые он использует.

Так что я могу сделать что-то вроде:

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

Затем в моем методе обработчика я могу проверить, было ли запущено событие из XYZ, проверьте, какое событие было запущено, получите параметр и сделайте что-нибудь.Например:

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

Я знаю, что это может быть достигнуто с помощью PostSharp, но я не могу его использовать.Мне нужно другое решение.


Связано


Обновление 2010-09-27 Я не смог найти решение и большеИнформация об этом, мне все еще нужна помощь.Добавлена ​​награда +150.

1 Ответ

4 голосов
/ 24 сентября 2010

Если вы просто добавляете вызов, это действительно просто - вам не нужно извлекать всех подписанных делегатов или что-то в этом роде;просто подпишитесь с отражением:

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

Теперь я сделал предположение о том, что вы хотите сделать, а именно - вызывайте свой метод один раз каждый раз, когда возникает событие.Это не то же самое, что вызов вашего метода один раз каждый раз, когда вызывается обработчик события , что и сделал бы ваш исходный код.Это намного сложнее ... но вам это действительно нужно?

(Если бы вы могли дать больше информации о большей цели, это действительно помогло бы.)

Обратите внимание, что код, который я здесь предоставил, должен работать независимо от того, как реализовано событие - он не полагается на то, что он является полевым событием.

РЕДАКТИРОВАТЬ: Хорошо,полный пример, показывающий, как вы можете добавить простой EventHandler делегат к любому событию, которое соответствует нормальному шаблону событий:

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

Обратите внимание, как мы должны построить точный тип требуемого делегата - но мы знаем, что мыэто можно сделать с помощью совместимого метода, при условии, что все соответствует нормальному шаблону событий.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...