イベント ハンドラーを動的にアタッチ/デタッチする利点はありますか?
ハンドラーを手動でデタッチすると、破棄されたオブジェクトへの参照が残っていないことを確認できますか?
イベント ハンドラーを動的にアタッチ/デタッチする利点はありますか?
ハンドラーを手動でデタッチすると、破棄されたオブジェクトへの参照が残っていないことを確認できますか?
Handles
この句は単なる構文糖衣であり、AddHandler
ステートメントをコンストラクターに挿入するものであると確信しています。このコードを使用してテストし、アプリケーション フレームワークを無効にして、コンストラクターに余分なものがないようにしました。
Public Class Form1
Public Sub New()
' This call is required by the Windows Form Designer. '
InitializeComponent()
' Add any initialization after the InitializeComponent() call. '
AddHandler Me.Load, AddressOf Form1_Load
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim breakpoint As Integer = 4
End Sub
End Class
ILは次のようになりました。
IL_0000: nop
IL_0001: ldarg.0
IL_0002: call instance void [System.Windows.Forms]System.Windows.Forms.Form::.ctor()
IL_0007: nop
IL_0008: ldarg.0
IL_0009: ldarg.0
IL_000a: dup
IL_000b: ldvirtftn instance void WindowsApplication1.Form1::Form1_Load(object,
class [mscorlib]System.EventArgs)
IL_0011: newobj instance void [mscorlib]System.EventHandler::.ctor(object,
native int)
IL_0016: call instance void [System.Windows.Forms]System.Windows.Forms.Form::add_Load(class [mscorlib]System.EventHandler)
'... lots of lines here '
IL_0047: ldarg.0
IL_0048: callvirt instance void WindowsApplication1.Form1::InitializeComponent()
IL_004d: nop
IL_004e: ldarg.0
IL_004f: ldarg.0
IL_0050: dup
IL_0051: ldvirtftn instance void WindowsApplication1.Form1::Form1_Load(object,
class [mscorlib]System.EventArgs)
IL_0057: newobj instance void [mscorlib]System.EventHandler::.ctor(object,
native int)
IL_005c: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Form::add_Load(class [mscorlib]System.EventHandler)
IL_0061: nop
IL_0062: nop
IL_0063: ret
} // end of method Form1::.ctor
IL_000b と IL_0051 の周りに 2 つの同一のコード ブロックがあることに注意してください。私はそれが単なる構文糖だと思います。
フィールドを as として宣言するWithEvents
と、コンパイラはその名前のプロパティを自動的に生成します。ゲッターはバッキング フィールドの値を返します。セッターはもう少し複雑です。最初に、バッキング フィールドに既に正しい値があるかどうかを確認します。もしそうなら、それは終了します。それ以外の場合、バッキング フィールドが null でない場合は、バッキング フィールドによって識別されるオブジェクトに対して、そのすべてのイベントに対する「RemoveHandler」リクエストを発行します。次に、バッキング フィールドが null ではないかどうかに関係なく、要求された値と等しくなるように設定します。最後に、新しい値が null でない場合、古い値がそうであったかどうかに関係なく、プロパティは、新しい値によって識別されるオブジェクトに対して、そのすべてのイベントに対して "AddHandler" 要求を発行します。
オブジェクトを放棄する前にオブジェクトのすべての WithEvents メンバーを に設定しNothing
、複数のスレッドで WithEvents メンバーを操作しないようにすれば、自動生成されたイベント コードはリークしません。
AddHandler と Handles のどちらを使用するかという問題ではありません。
イベント ハンドラーへの参照がガベージ コレクションに干渉することが懸念される場合は、ハンドラーがどのようにアタッチされたかに関係なく、RemoveHandler を使用する必要があります。フォームまたはコントロールの Dispose メソッドで、ハンドラーをすべて削除します。
Windows フォーム アプリ (.NET 1.1 日) で、他の参照を持たないコントロールでイベント ハンドラーが呼び出される状況がありました (そして、すべての意図と目的のために死んでいて、GC されたと思っていたでしょう)。 -- デバッグが非常に困難です。
再利用しないコントロールのハンドラーを削除するには、RemoveHandler を使用します。
コントロールを手動で作成するときにハンドラーを手動でアタッチします (たとえば、データベース レコードごとに TextBox を動的に作成するなど)。まだ処理する準備が整っていないものをハンドラーが処理しているときは、ハンドラーを手動でデタッチします (おそらく、間違ったイベントを使用しているためですか? :))
イベント ハンドラーを動的にアタッチ/デタッチするのは、寿命の長いオブジェクトが多くの寿命の短いオブジェクトによって消費されるイベントを公開している場合にのみ使用できることがわかりました。他のほとんどの場合、2 つのオブジェクトはほぼ同時に破棄され、CLR は独自にクリーンアップするのに十分な作業を行います。
ほとんどの場合、フレームワークがそれを処理します。
イベントを手動でデタッチすることは、メモリ リークを防ぐために重要な場合があります。別のオブジェクトによって起動されたイベントに接続するオブジェクトは、イベントを起動したオブジェクトがガベージ コレクションされるまでガベージ コレクションされません。言い換えれば、「イベント発生器」は、それに接続されているすべての「イベントリスナー」への強力な参照を持っています。