【テクニカル・上級編】VB.NETでのイベント駆動プログラミング入門:AddHandlerとHandlesキーワードの使い分け – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

イベント駆動の深淵:`Handles`と`AddHandler`を極め、VB.NETのメモリ管理を制する

VB.NETを扱うエンジニアにとって、イベントの配線(Wiring)は避けて通れない。しかし、多くの現場で「なんとなく」使い分けられ、結果としてメモリリークという名の時限爆弾を抱えたシステムが放置されている。

今日は、Visual Studioが自動生成する`Handles`句の甘美な罠と、動的生成の切り札である`AddHandler`の真実について、アーキテクトの視点から紐解こう。

1. Handlesキーワード:静的な結合の限界とリスク

`Handles`は、コンパイル時にイベント購読を確定させる「静的バインディング」だ。コードが読みやすく、設計意図が明確になるため、UIの初期定義においては最適解である。

.net
‘ フォーム上のButton1がクリックされた際の標準的なハンドリング
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
‘ ここでWindows APIを呼ぶ際も、UIスレッドの占有には注意が必要だ
End Sub

なぜこれが「罠」となり得るのか?

`Handles`は、オブジェクトが存在する限り購読を維持する。もし、動的に生成したフォームやコントロールを`Close()`や`Dispose()`しても、その親(Owner)が生きている限り、イベントの結びつきがGC(ガベージコレクタ)の回収を阻害することがある。特に、UI部品が複雑な外部リソースを保持している場合、メモリリークの温床となる。

2. AddHandler:動的生成の必須スキルと「解放」の責務

一方で、動的に生成するコントロールには`AddHandler`が必須だ。これは実行時にデリゲートを生成し、イベントチェーンに接続する「動的バインディング」である。

ここで重要なのは、「AddHandlerしたら、必ずRemoveHandlerする」というエンジニアとしての矜持だ。

実践:動的生成とメモリ安全な解除のパターン

.net
Public Class DynamicManager
Private WithEvents _dynamicButton As Button

Public Sub CreateButton()
_dynamicButton = New Button() With {.Text = “実行”}

‘ 接続:イベントハンドラを登録
AddHandler _dynamicButton.Click, AddressOf DynamicButton_Click
End Sub

Private Sub DynamicButton_Click(sender As Object, e As EventArgs)
‘ 処理内容
End Sub

‘ 終了処理:メモリリークを防ぐための絶対的な儀式
Public Sub DisposeButton()
If _dynamicButton IsNot Nothing Then
‘ 購読を明示的に解除する
RemoveHandler _dynamicButton.Click, AddressOf DynamicButton_Click
_dynamicButton.Dispose()
_dynamicButton = Nothing
End If
End Sub
End Class

この`RemoveHandler`を怠ることは、レガシーシステムにおける「メモリの肥大化」という最大の負債を生成することに他ならない。

3. シニアエンジニアが知るべき「Windows API」との連動

大規模な業務システムでは、`.NET`のラッパーを超えてWindows APIを直接叩く必要がある場面がある。その際、`AddHandler`で紐付けたイベント内でAPI呼び出しを行う際は、「再入可能性(Reentrancy)」に注意せよ。

特に、`SendMessage`や`PostMessage`を用いてイベントを擬似的に発生させる際、イベントハンドラ内でさらにイベントをトリガーするとスタックオーバーフローやデッドロックを誘発する。これを防ぐには、フラグによる排他制御、あるいは`BeginInvoke`による非同期実行が必須だ。

4. アーキテクトからの提言:設計指針

1. 基本はHandles、動的はAddHandler: 原則を崩すな。設計が複雑になるなら、それはイベント駆動ではなく「Observerパターン」を明示的に実装すべき合図だ。
2. Disposeパターンの徹底: IDisposableを実装するクラスでイベントを購読する場合、`Dispose`メソッド内で必ず`RemoveHandler`を実行せよ。
3. 匿名メソッドの誘惑に勝て: `AddHandler btn.Click, Sub(s, e) … End Sub` と書きたくなる気持ちは分かる。だが、これを行うと`RemoveHandler`が物理的に不可能になる。名前付きメソッド(AddressOf)を使うことこそが、プロフェッショナルのコードだ。

結びに代えて

VB.NETは古臭い言語ではない。`.NET Core` / `.NET 8`以降の現代的なランタイム環境において、正しく扱えば極めて強力な開発生産性を発揮する。

イベント配線は、システムという巨大な有機体の「神経系」だ。ここを疎かにする者は、いずれメモリの闇に飲まれる。コードを記述する際、一秒先のメモリの状態を想像せよ。それが、システムを支配する唯一の道だ。

タイトルとURLをコピーしました