4

イベント ハンドラーにデリゲートを追加するときはいつでも、後でそれを削除する必要がありますよね? では、匿名メソッドをイベントにアタッチすると、後で削除できないため、イベント ハンドラー リークが発生するのでしょうか? http://msdn.microsoft.com/en-us/library/0yw3tz5k%28VS.80%29.aspxからのこのコード サンプルは、これが問題ないプラクティスであることを暗示しているようです。

// Create a handler for a click event
button1.Click += delegate(System.Object o, System.EventArgs e)
                   { System.Windows.Forms.MessageBox.Show("Click!"); };

これは本当に大丈夫な習慣ですか?

4

4 に答える 4

11

イベントハンドラーにデリゲートを追加するときはいつでも、後で削除する必要がありますよね?

必ずしもそうではありません。多くの場合、イベント自体を発生させることができる限り、イベントハンドラーを有効なままにしておく必要があります。これは、UIでは確かに非常に一般的です。

これは本当に大丈夫ですか?

もちろん、ハンドラーのフックを解除する必要がない限り。イベントハンドラーのフックを解除するポイントについて考えてください。「フォーム(またはボタンなど)がガベージコレクションに適している場合」の場合、ハンドラーを削除するとどのようなメリットがありますか?フォームでガベージコレクションするだけです...

于 2012-11-29T22:00:58.447 に答える
6

イベント ハンドラーにデリゲートを追加するときはいつでも、後でそれを削除する必要がありますよね?

まあ、いつもではありません。追加するイベント ハンドラーを削除する理由は 2 つあります。

  1. 同じインスタンスに短命のハンドラーを常に追加しています。それらを削除しなかった場合、ほとんどのハンドラーが不要になったときに、ますます多くのハンドラーが追加されます。
  2. ハンドラーは、イベントが属するオブジェクトのライフタイムよりもはるかに短いライフタイムのオブジェクトへの参照を内部的に保持しており、他のオブジェクトがスコープ外になると、イベント ハンドラーは呼び出されません (または呼び出されません)。 . イベント ハンドラーをアタッチしたままにしておくと、イベント ハンドラーが必要以上に長くメモリ内にとどまるか、「古くなった」オブジェクトを使用してしまう可能性があり、もう使用すべきではありません。(たとえば、リソースが既に破棄されている場合は、そのイベントをもう発生させたくないでしょう。)

#2 が問題である理由は、ガベージ コレクションが C# でどのように機能するかによるものです。スコープ内に 100% 確実に存在するすべてのオブジェクトを「生きている」とマークし、すべての生きているオブジェクトのすべての参照を追跡するまで、それらの「生きている」オブジェクトのいずれかが「生きている」として参照するすべてのものを追跡します。「生きている」とマークされなかったものはすべて「死んでいる」と見なされ、ガベージ コレクションの対象となります。

イベントにアタッチされたイベント ハンドラーがある場合、デリゲートには、オブジェクトのインスタンスとそのオブジェクトで実行するメソッドの 2 つが含まれます。その参照されたオブジェクトは、次のいずれかになるまでガベージ コレクションできません。

  1. イベントが発生したオブジェクトは、もはや「生きていません」。
  2. イベント ハンドラー (つまり、デリゲートへの参照) を削除して、オブジェクトを早期に解放できるようにします。

とはいえ、かなりの割合のケースがこれらのいずれにも当てはまらないため、わざわざイベント ハンドラーを削除する必要はありません。

例として、イベント オブジェクトが範囲外になる直前にイベント ハンドラーを削除する人をよく見かけます。それは無意味です。オブジェクトが範囲外にある場合、参照を保持しても問題はありません...何でも。

ここで、イベント ハンドラーのサブスクライブを解除する必要がある数少ない状況の 1 つで、匿名メソッドを使用している場合は、そうする必要はありません。名前付きメソッドにすることができるクラスを作成し、それを使用するだけです。

于 2012-11-29T22:02:42.837 に答える
3

匿名メソッド(またはラムダ式)を使用してイベントをサブスクライブすると、メモリリークが発生する可能性がありますが、この場合はそうではありません。

この場合、コンパイラは現在のクラス内に匿名メソッドを生成し、ボタンがオブジェクトよりも長く存続しない限り、メモリ関連の問題は発生しません。

于 2012-11-29T22:01:48.643 に答える
3

あなたが述べたように、イベントハンドラーとしてインライン匿名デリゲートを使用すると、それらのハンドラーが削除されなくなります。特にデリゲートが別のオブジェクトからのものである場合、これは確かに問題を引き起こす可能性があります。公開イベント E を持つクラス A があり、匿名ハンドラー H をアタッチするクラス B (A との包含/包含関係を持たない) によって E がサブスクライブされている場合、H は技術的には B のメンバーであり、 (A のイベントにアタッチされることによって) 参照されるため、B は GC されません。B の存続期間は A の存続期間に結び付けられ、望まない、または予期しない場合は、メモリ リークが発生します。

さて、それは常に問題ではありません。B に A が含まれている場合、またはその逆の場合、A と B の有効期間はいずれにせよ既に関連付けられています。あなたの場合、ボタン自体は、ハンドラーをアタッチしたコントロールを含む前に破棄される可能性が非常に高くなります。同様に、A と B は存続期間の長いオブジェクト (たとえば、プログラムのメイン フォームとデータ リポジトリ) である可能性があり、いずれにしてもプログラムが終了するまで GC されません。AとB の寿命の違いを気にする必要がなく、H の動作を停止するために H を E から切り離す必要がない限り、好きな匿名ハンドラーをすべてアタッチできます。

于 2012-11-29T22:19:56.417 に答える