C#从计时器添加和删除事件

时间:2009-02-16 17:45:34

标签: c# event-handling

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

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。

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

7 个答案:

答案 0 :(得分:8)

我相信这段代码:

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对象。

答案 1 :(得分:3)

初始代码工作正常,只要传递到MyDelegateev的{​​{1}}'addEvent'是同一个对象实例(例如,如果有的话)包含实例的类级removeEvent字段,或者如果您遵循其他几个人的建议并将MyDelegate对象保留在字典中。

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

MyDelegate

在哪种情况下,addEvent(new MyDelegate(this.HandlerMethod)); // ... do some stuff removeEvent(new MyDelegate(this.HandlerMethod)); addEvent正在创建指向不同方法地址的removeEvent委托,即使这些委托依次指向同一方法(EventHandler) 。这是因为this.HandlerMethodEventHandler创建的add代理在不同remove个实例上指向MyDelegate.Invoke()方法,而不是直接指向{{1}的地址}}

答案 2 :(得分: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(...));

答案 3 :(得分:1)

您应该只能通过引用偶数处理方法的名称来取消订阅:

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

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

我不知道你做错了什么,但我会用于Timers的常用方法是订阅Tick事件,然后在你不想接收时禁用Timer事件,当你这样做时重新启用。

如果您有多个事件处理程序连接到事件,可能无法帮助您,但希望可以使用。

答案 6 :(得分:0)

这应该有效:

private void timer_Tick(object sender, EventArgs e)
{
    try
    {
        // Disallow re-entry
        timer.Tick -= timer_Tick;
        . . .
    }
    finally
    {
        timer.Tick += timer_Tick;
    }
}