【问题标题】:How can I change a method assigned on an event?如何更改分配给事件的方法?
【发布时间】:2010-09-24 13:16:18
【问题描述】:

我正在获取具有特定属性的所有事件,并且我想修改这些事件,添加对另一个方法的调用。

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

        **/
    }
}

我想要的基本上就是上面评论的。 “修改”事件的订阅方法。我想我不能订阅它,因为我需要将方法正在传递的参数传递给大处理程序(新方法)。

另一个基于on this question 的示例。我想转换这个:

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 赏金。

【问题讨论】:

    标签: c# .net reflection


    【解决方案1】:

    如果您只是添加一个调用,那真的很简单——您不需要获取所有订阅的代表或类似的东西;只需通过反射订阅:

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

    注意我们必须如何构造所需的确切类型的委托 - 但我们知道我们可以通过 兼容 方法来做到这一点,只要一切都遵循正常的事件模式。

    【讨论】:

    • 我添加了“更大的目标”。您当前的答案很接近,抱歉我忘了提到我需要获取方法订阅正在传递的参数。我添加了更多信息...
    • @BrunoLM:从外观上看,这只是构建合适的委托的问题。我现在要出去,不过一个小时后我会去看看,如果那时你还没有解决的话。 Lambda 表达式很可能是你的朋友。
    • @Jon Skeet:我还是不明白......我怎样才能得到事件正在使用的对象?我能想到的唯一方法是修改相同的方法。我需要类似BigHandler(eventName, params_from_the_event.ToArray&lt;object&gt;()
    • @BrunoLM:“事件正在使用的对象”是什么意思?你的意思是(object sender, EventArgs e) 部分(或任何事件的签名)?
    • @Jon Skeet:随便签名。我想获取所有参数并放入object[]
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-06-10
    • 1970-01-01
    • 1970-01-01
    • 2018-07-21
    • 2012-02-17
    • 2014-03-29
    • 1970-01-01
    相关资源
    最近更新 更多