web-dev-qa-db-ja.com

匿名イベントハンドラーの追加と削除

これが実際に機能するかどうか疑問に思っていましたか?

private void RegisterKeyChanged(T item) 
{
    item.OnKeyChanged += (o, k) => ChangeItemKey((T)o, k);
}

private void UnRegisterKeyChanged(T item) 
{
    item.OnKeyChanged -= (o, k) => ChangeItemKey((T)o, k);
}

コンパイラーは、イベントハンドラーが同じであることをどのように認識しますか?これもお勧めですか?

53
HaxElit

これについて説明するMSDNページがあります。

イベントの購読および購読解除方法

特に注意してください:

後でイベントの購読を解除する必要がない場合は、追加の代入演算子(+ =)を使用して、イベントに匿名メソッドをアタッチできます。

そしてまた:

匿名関数を使用してサブスクライブする場合、イベントから簡単にサブスクライブ解除できないことに注意することが重要です。このシナリオでサブスクライブを解除するには、イベントをサブスクライブするコードに戻り、匿名メソッドをデリゲート変数に格納してから、デリゲートをイベントに追加する必要があります。一般的に、コードの後半でイベントのサブスクリプションを解除する必要がある場合は、イベントのサブスクライブに匿名関数を使用しないことをお勧めします。

59
Ryan Lundy

興味のある方は、このような匿名イベントハンドラーを追加および削除できます

public class Musician
{
    public void TuneGuitar()
    {
        Metronome metronome = new Metronome();

        EventHandler<EventArgs> handler = null;
        handler = (sender, args) =>
        {
            // Tune guitar
            // ...

            // Unsubscribe from tick event when guitar sound is perfect
            metronome.Tick -= handler;
        };

        // Attach event handler
        metronome.Tick += handler;
    }
}

public class Metronome
{
    event EventHandler<EventArgs> Tick;
}

更新:C#7.0では、 ローカル関数 をサポートしているため、TuneGuitarメソッドは次のように記述できます。

public void TuneGuitar()
{
    Metronome metronome = new Metronome();

    void handler(object sender, EventArgs args)
    {
        // Tune guitar
        // ...

        // Unsubscribe from tick event when guitar sound is perfect
        metronome.Tick -= handler;
    };

    // Attach event handler
    metronome.Tick += handler;
}
12
sb.olofsson

イベントハンドラの登録を解除する必要がある場合は、具体的なデリゲートへの明確な参照が必要です。見つめている - Delegate.Equality デリゲートは、参照の等式を使用して比較されるだけではありませんが、匿名デリゲートでは問題になりません。

匿名デリゲートの場合、コンパイラは(基本的に)デリゲート本体が同じ場合でも、各匿名デリゲートに対して新しい「非匿名」デリゲートを作成します。このため、フレームワークは、指定したコード例を使用しても、サブスクライブを解除するデリゲートを見つけられません。

6
Dan Herbert

あなたが宣言した2つのラムダ式(およびデリゲート)は実際には異なるオブジェクトであり、異なる参照を返すため、それはうまくいきません。したがって、ハンドラー(-=)は常に失敗します。

この問題の一般的な解決策(ハンドラーを削除する必要がある場合)は、単にランバ式を適切なメソッドにリファクタリングすることです。別の方法は、イベントハンドラーデリゲートのクラス変数を維持し、これを追加および削除することですが、私は個人的にはそのファンではありません。 (もしあれば、通常のメソッドを作成するよりも手間がかかります。)

3
Noldorin

これがうまくいくとは思わない。本当にイベントから登録解除する必要がある場合は、匿名デリゲートの代わりに後で登録解除できる明示的なイベントハンドラを指定する必要があります。

2
Dan Auclair

Delegate.Equalityのドキュメントを確認すると、それらが参照によって比較されていないことがわかります。

1
Codism