1. ホーム
  2. c#

[解決済み】ラムダのイベントハンドラを削除する方法【重複あり

2022-04-01 11:23:55

質問

<余談
この質問には、すでにここで回答があります :
クローズド 3ヶ月前 .

最近、ラムダを使って簡単なイベントハンドラを作ることができることを発見しました。例えば、次のようなクリックイベントを受信することができます。

button.Click += (s, e) => MessageBox.Show("Woho");

でも、どうやって配信を停止するのでしょうか?

解決方法は?

C#の仕様では、2つの匿名関数(匿名メソッドまたはラムダ式)がある場合、そのコードから同等のデリゲートを作成できる場合とできない場合があると明示されています(IRC)。(2つのデリゲートは、同じターゲットを持ち、同じメソッドを参照していれば、等しい)。

念のため、使用したデリゲートインスタンスを覚えておく必要がありますね。

EventHandler handler = (s, e) => MessageBox.Show("Woho");

button.Click += handler;
...
button.Click -= handler;

(仕様の該当部分が見つからないのですが、C#コンパイラが積極的に等しいデリゲートを作成しようとするのを見ると、かなり驚きますね。確かにそれに頼るのは賢明ではないだろう)。

それが嫌なら、メソッドを抽出する必要があります。

public void ShowWoho(object sender, EventArgs e)
{
     MessageBox.Show("Woho");
}

...

button.Click += ShowWoho;
...
button.Click -= ShowWoho;

ラムダ式を使って自分自身を削除するイベントハンドラを作りたい場合は、少し厄介です。ラムダ式自体の中でデリゲートを参照する必要がありますが、単純に "ローカル変数を宣言してラムダ式を使ってそれに代入する" では、変数は確実に代入されないため、それを行うことができません。このような場合は、まず変数にNULL値を代入して回避するのが一般的です。

EventHandler handler = null;
handler = (sender, args) =>
{
    button.Click -= handler; // Unsubscribe
    // Add your one-time-only code here
}
button.Click += handler;

残念ながら、イベントはきれいに表現できないので、これをメソッドにカプセル化することも簡単ではありません。一番近いのは次のようなものだろう。

button.Click += Delegates.AutoUnsubscribe<EventHandler>((sender, args) =>
{
    // One-time code here
}, handler => button.Click -= handler);

の中で実装するのは難しいでしょう。 Delegates.AutoUnsubscribe というのも、新しい EventHandler (これは単なる一般的な型引数になる)。可能ですが、面倒です。