【実務・中級編】上級プロフェッショナル向け:VB.NETでのExceptionDispatchInfoを活用したスレッド間例外の正確な再スローとエラー伝播 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

なぜ、あなたの書く例外処理は「現場」で役に立たないのか?

業務自動化ツールを開発していると、必ずぶち当たる壁がある。「非同期処理中に発生した例外が、呼び出し元に届かない」あるいは「スタックトレースが破壊され、原因特定が不可能になる」という問題だ。

多くの初学者は、安易に `Try-Catch` で囲み、`Throw ex` を記述する。これこそが、デバッグを迷宮入りさせる最大級の戦犯だ。 `Throw ex` を行うと、スタックトレースがその行でリセットされ、例外の発生源が隠蔽される。

プロフェッショナルな現場では、例外は「握りつぶす」のではなく、「文脈を維持したまま、しかるべき場所へ転送する」必要がある。今日は、`.NET` の深淵に触れるテクニック `ExceptionDispatchInfo` を用いた、堅牢なエラー伝播の極意を伝授する。

1. スタックトレースを殺さない「伝播」の技術

非同期タスク内で例外が発生した際、それをメインスレッドで再スローしたいとしよう。単にキャッチして再度投げるだけでは、CLR(共通言語ランタイム)は「今、この場所で発生した例外」として処理してしまい、元のスタックトレースは消滅する。

ここで登場するのが `System.Runtime.ExceptionServices.ExceptionDispatchInfo` だ。これは、例外の「状態(スタックトレースや詳細情報)」を凍結し、別の場所で解凍して再スローするための強力なツールである。

実践コード:堅牢な非同期例外ハンドリング

以下のコードは、スレッド間を跨いでもスタックトレースを完璧に保持するテンプレートだ。

Imports System.Runtime.ExceptionServices
Imports System.Threading.Tasks

Public Class AsyncExceptionHandler
”’

”’ 非同期処理を実行し、例外発生時にはスタックトレースを維持したまま再スローする
”’

Public Shared Async Function ExecuteWithSafety(action As Func(Of Task)) As Task
Dim capturedException As ExceptionDispatchInfo = Nothing

Try
Await action()
Catch ex As Exception
‘ 例外の状態をキャプチャする(スタックトレースを凍結)
capturedException = ExceptionDispatchInfo.Capture(ex)
End Try

‘ キャプチャした例外が存在すれば、呼び出し元で再スロー
If capturedException IsNot Nothing Then
capturedException.Throw()
End If
End Function
End Class

2. 実務で「落ちない」システムを構築するための設計指針

単にコードをコピペするだけでは足りない。業務自動化ツールを「資産」にするためには、以下の3原則を徹底せよ。

  • 「例外は末端で処理するな」: 下位層(DBアクセスやファイルIO)で例外を握りつぶすと、何が起きたか分からなくなる。下位層は例外を「ログ記録」または「再スロー」し、上位層(UI層やコントローラー層)で集約してユーザーに通知するのが正解だ。
  • 非同期の罠を回避せよ: `Task` を返すメソッドを呼ぶ際、`Await` を忘れると「ファイア・アンド・フォーゲット(投げっぱなし)」になり、例外がスレッドをクラッシュさせる。必ず `Await` するか、例外監視用のタスク管理ロジックを実装すること。
  • リソース管理の自動化: ファイルやDB接続を扱う際は、必ず `Using` ステートメントを使用する。例外が発生しても確実にリソースが開放される保証こそが、長時間稼働する自動化ツールの信頼性に直結する。

3. 現場でそのまま使える:DB連携時のエラー伝播例

例えば、データベース接続でタイムアウトが発生した際、単なるエラーログだけでなく、「どこの処理で」「何が起きたか」を保持したままメインスレッドへ戻す設計だ。

Public Async Function ProcessDatabaseTaskAsync() As Task
Try
Await AsyncExceptionHandler.ExecuteWithSafety(Async Function()
‘ ここでDB操作を行う
‘ 例: Dim result = db.ExecuteQuery(“SELECT…”)
‘ もしここでエラーが出ても、呼び出し元に正確なスタックトレースが伝播する
End Function)
Catch ex As Exception
‘ ここで初めてログを出力し、ユーザーへ通知する
‘ この時点の ex.StackTrace には、本来の発生源が含まれている
Logger.Log(ex)
MsgBox(“致命的なデータベースエラーが発生しました。” & vbCrLf & ex.Message)
End Try
End Function

最後に:エンジニアとしての矜持

コードを書くことは、誰にでもできる。しかし、「例外を制御し、システム全体の死を防ぐ」ことは、設計者の技量だ。

`ExceptionDispatchInfo` は、魔法ではない。CLRがどのようにメモリを管理し、スタックを構築しているかを理解した者が使う、外科手術のような精密なツールだ。

あなたが次に書く業務ツールには、この「スタックの断絶を許さない」という気概を込めてほしい。そうすれば、バグ調査に費やす時間は劇的に減り、あなたはより創造的な開発に集中できるはずだ。

さあ、コードを書き換えよう。あなたのアプリケーションに、真の堅牢性を。

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