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が削除されることはありません。必要な機能を取得するには、MyDelegatesではなくEventHandlersを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

問題は、次のように、コードが呼び出してaddEvent、ハンドラー メソッドを指すremoveEvent新しいインスタンスを渡していることだと思います。MyDelegate

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

この場合addEvent、 とは、異なるメソッド アドレスを指すデリゲートをremoveEvent作成していますが、これらのデリゲートは同じメソッドを指しています ( )。これは、とcreateのデリゲートが、 のアドレスを直接指すのではなく、異なるインスタンスのメソッドを指すためです。EventHandlerthis.HandlerMethodEventHandleraddremoveMyDelegate.Invoke()MyDelegatethis.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

何が間違っているのかわかりませんが、タイマーに使用する通常のアプローチは、イベントをサブスクライブし、Tickイベントを受信したくない場合はタイマーを無効にし、次の場合は再度有効にすることです。行う。

複数のイベントハンドラーがイベントに接続されている場合は役に立たない可能性がありますが、うまくいけばいくつかの用途があります。

于 2009-02-16T18:01:40.177 に答える
0

次のように、処理メソッドの名前を参照することで、サブスクライブを解除できるはずです。

public void removeEvent(MyDelegate ev)
{
    myTimer.Tick -= ev as EventHandler;
}
于 2009-02-16T17:51:24.287 に答える
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オブジェクトを削除しようとしています。

このタイプのセットアップを使い続けたい場合は、EventHandlerをディクショナリに貼り付けることができます。addEventメソッドで、新しく作成したEventHandlerをディクショナリに貼り付け、removeEventメソッドで、新しいディクショナリをインスタンス化する代わりに、ディクショナリからEventHandlerをプルして削除します。

于 2009-02-16T17:55:45.277 に答える