8

我正在尝试从计时器中添加和删除事件,并且我有以下代码:

Timer myTimer = new Timer(); // Windows.Forms Timer

public void addEvent(MyDelegate ev)
{
    myTimer.Tick += new EventHandler(ev);
}

public void removeEvent(MyDelegate ev)
{
    myTimer.Tick -= new EventHandler(ev);
}

我不知道如果我在尝试以这种方式添加和删除代表时做了任何愚蠢的事情,我可以添加代表并让它们按预期触发。但是,当我尝试删除这些事件时,它们会继续在 Timers Tick 上触发。

任何人都可以看到明显的错误吗?

4

7 回答 7

12

我相信这段代码:

myTimer.Tick -= new EventHandler(ev);

创建一个新的 EventHandler 对象。它永远不会删除现有的 EventHandler。要获得您想要的功能,您应该将 EventHandlers 而不是 MyDelegates 传递给 add 和 remove 方法:

Timer myTimer = new Timer(); // Windows.Forms Timer

public void addEvent(EventHandler ev)
{
    myTimer.Tick += ev;
}

public void removeEvent(EventHandler ev)
{
    myTimer.Tick -= ev;
}

调用代码必须跟踪添加的 EventHandler,以便在取消订阅时可以传入相同的 EventHandler 对象。

于 2009-02-16T18:00:28.307 回答
3

初始代码工作正常,只要传入的 ' '是MyDelegate同一个对象实例(例如,如果有一个包含该实例的类级字段,或者如果您在这里遵循其他几个人的建议并保留该对象(s) 在字典中)。evaddEventremoveEventMyDelegateMyDelegate

我怀疑问题是代码调用addEventremoveEvent传递MyDelegate指向某个处理程序方法的新实例,如下所示:

addEvent(new MyDelegate(this.HandlerMethod));
// ... do some stuff
removeEvent(new MyDelegate(this.HandlerMethod));

在这种情况下addEvent,并且removeEvent正在创建EventHandler指向不同方法地址的委托,即使这些委托又指向同一个方法(this.HandlerMethod)。这是因为EventHandler委托addremovecreate 指向MyDelegate.Invoke()不同MyDelegate实例上的方法,而不是直接指向this.HandlerMethod.

于 2009-02-16T21:55:54.667 回答
2

您的问题来自于使用辅助方法来执行此操作。没有它们,它可以按预期工作,有了它们,它不知道要解开什么。

要解决此问题,您需要维护一个字典,其值是在挂钩方法中创建的 EventHandler,以便以后可以删除该值。

就像是:

var handlers = new Dictionary<MyDelegate, EventHandler>();

public void addEvent(MyDelegate ev)
{
    var handler = new EventHandler(ev);
    handlers.Add(ev, handler);
    myTimer.Tick += handler;
}

public void removeEvent(MyDelegate ev)
{
    myTimer.Tick -= handlers[ev];
}

如果元素存在,您应该添加适当的检查。

您还可以更改参数类型,它将按预期工作。

public void addEvent(EventHandler ev)
{
    myTimer.Tick += ev;
}

public void removeEvent(EventHandler ev)
{
    myTimer.Tick -= ev;
}

addEvent(new EventHandler(...));
removeEvent(new EventHandler(...));
于 2009-02-16T18:00:36.527 回答
0

您应该可以通过引用您的偶数处理方法的名称来取消订阅,如下所示:

public void removeEvent(MyDelegate ev)
{
    myTimer.Tick -= ev as EventHandler;
}
于 2009-02-16T17:51:24.287 回答
0

我不知道你做错了什么,但我对定时器使用的通常方法是订阅Tick事件,然后在你不想接收事件时禁用定时器,当你重新启用做。

如果您有多个事件处理程序连接到事件,可能对您没有帮助,但希望有一些用处。

于 2009-02-16T18:01:40.177 回答
0

这应该有效:

private void timer_Tick(object sender, EventArgs e)
{
    try
    {
        // Disallow re-entry
        timer.Tick -= timer_Tick;
        . . .
    }
    finally
    {
        timer.Tick += timer_Tick;
    }
}
于 2016-03-02T23:55:34.793 回答
-1

添加和删​​除事件处理程序时,您每次都在为您的委托创建一个新的包装器。因此,在您的 remove 方法中,它试图删除一个的EventHandler 对象,该对象从未作为事件的侦听器首先添加。

如果您想继续使用这种类型的设置,您可以将您的 EventHandlers 粘贴到字典中。在 addEvent 方法中,将新创建的 EventHandler 粘贴到字典中,在 removeEvent 方法中,从字典中拉出 EventHandler 并将其删除,而不是实例化一个新的。

于 2009-02-16T17:55:45.277 回答