正在注销事件处理程序

本文关键字:程序 事件处理 注销 | 更新日期: 2024-09-27 01:23:17

我有一个类,当收到一些消息时,它可以引发事件。此外,它还可以订阅来自同一类的其他实例的消息。(请参阅下面的缩短类。)我在注销事件处理程序时遇到问题。。。调试时,我可以看到Delegate.Remove被调用(在另一个语法版本中),但调用列表中没有删除任何内容。。。

class MyClass
{
    private event EventHandler<EventArgs> MessageReceived;
    public void SubscribeToMessages(Action<object, EventArgs> eventHandler)
    {
        this.MessageReceived += new EventHandler<EventArgs>(eventHandler);
    }
    public void UnsubscribeFromMessages(Action<object, EventArgs> eventHandler)
    {
        this.MessageReceived -= new EventHandler<EventArgs>(eventHandler);
    }
    private void MessageFromOtherObject_Received(object sender, EventArgs arg)
    {
    }
    public void StartListeningToObject(MyClass obj)
    {
        obj.SubscribeToMessages(MessageFromOtherObject_Received);
    }
    public void StopListeningToObject(MyClass obj)
    {
        obj.UnsubscribeFromMessages(MessageFromOtherObject_Received);
    }
}
void Test()
{
    MyClass mainObj = new MyClass();
    MyClass otherObj = new MyClss();
    mainObj.StartListeningToObject(otherObj);
    //...
    mainObj.StopListeningToObject(otherObj);
}

因此,在调用StopListeningToObject()时,我可以看到它试图删除处理程序,但它仍然存在,并且事件仍然在mainObj上被触发。。。

现在根据我所知道的和我读到的

this.MessageReceived -= new EventHandler<EventArgs>(eventHandler);

语法应该可以正常工作,但它似乎认为这显然是一个不同的委托。

我做错了什么???

非常感谢您的建议!

正在注销事件处理程序

您的代码被破坏的原因是您创建了以下两种方法:

public void SubscribeToMessages(Action<object, EventArgs> eventHandler)
{
    this.MessageReceived += new EventHandler<EventArgs>(eventHandler);
}
public void UnsubscribeFromMessages(Action<object, EventArgs> eventHandler)
{
    this.MessageReceived -= new EventHandler<EventArgs>(eventHandler);
}

而不是仅仅从事件中添加/删除处理程序(这是你应该做的),你实际上在做一些完全不同的事情。

SubscribeToMessages正在添加一个事件处理程序,该事件处理程序在被调用时将调用eventHandler委托的Invoke方法。

当您调用UnsubscribeFromMessages时,您正试图删除处理程序,该处理程序的主体是对已传递的eventHandler实例的Invoke方法的调用。但是,您将不同的Action实例传递给这两个方法调用中的每一个(即使这两个不同的操作都指向同一个方法/对象对),因此您试图添加/删除的事件处理程序都指向不同的Action实例,因此不被视为相等。

如果您只是直接向事件添加/删除处理程序,而不是添加第二层间接处理程序,即添加一个调用事件处理程序的事件处理程序,该事件处理程序调用实际方法,那么您就可以了。

或者,不要让您正在使用的订阅/取消订阅方法具有与您的事件不同的委托。让它们接受实际事件类型的委托,这样您就可以简单地添加/删除它们,这也将删除额外的间接层。

为什么如此复杂?为什么你需要私人活动?保持简单!

    class Program
{
    static void Main(string[] args)
    {
    }
    void Test()
    {
        MyClass mainObj = new MyClass();
        MyClass otherObj = new MyClass();
        mainObj.StartListeningToObject(otherObj);
        //...
        mainObj.StopListeningToObject(otherObj);
    }
}
class MyClass
{
    public event EventHandler<EventArgs> MessageReceived;
    private void MessageFromOtherObject_Received(object sender, EventArgs arg)
    {
        //do some work
    }
    public void StartListeningToObject(MyClass obj)
    {
        obj.MessageReceived += MessageFromOtherObject_Received;
    }
    public void StopListeningToObject(MyClass obj)
    {
        obj.MessageReceived -= MessageFromOtherObject_Received;
    }
}

顺便说一句,别忘了触发那个事件!

问题是,注册的委托的目标实际上是eventHandler.Invoke,而不是您想要委托的目标/方法。然后,当您尝试删除处理程序时,它是针对新委托对象的Invoke方法的,因此它们不被视为相等,并且保留原始注册。您要委派给MessageFromOtherObject_Received。有三种选择

  • 直接在Start/StopListeningToObject中进行添加/删除
  • UnsubscribeFrom/SubscribeToMessages的参数类型更改为EventHandler<EventArgs>
  • 通过创建具有正确目标/方法的代表

    var handler = (EventHandler<EventArgs>)Delegate.CreateDelegate(typeof(EventHandler<EventArgs>), eventHandler.Target, eventHandler.Method);
    this.MessageReceived += handler;
    

    在CCD_ 17中使用类似的代码来移除处理程序。