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

スポンサーリンク

スタックトレースを殺すな:VB.NETで例外を「正確に」再スローする極意

業務自動化ツールを開発していると、必ず直面する壁がある。それは「非同期処理で発生したエラーが、どこで起きたのか分からない」という問題だ。

多くのエンジニアは、`Try-Catch`で捕まえた例外をそのまま `Throw ex` して済ませる。しかし、これこそがデバッグ地獄への片道切符だ。`Throw ex` を実行した瞬間、元のスタックトレースは破壊され、エラーの発生源は「再スローした場所」に上書きされてしまう。

今日は、`.NET`の深淵に触れる技術、`System.Runtime.ExceptionServices.ExceptionDispatchInfo` を駆使し、別スレッドの例外をメインスレッドで「当時のまま」再現する方法を授けよう。

1. なぜ「Throw ex」が最悪の選択肢なのか

初心者がやりがちな以下のコードを見てほしい。

Try
‘ データベース接続処理など
Catch ex As Exception
‘ ここでThrow exをすると…
Throw ex ‘ 致命的な設計ミス!
End Try

これを行うと、スタックトレースの起点が「再スローした行」に置き換わる。数千行ある業務コードの中で、真の発生源(例:DBの接続文字列不正なのか、SQLの文法エラーなのか)を特定するコストは跳ね上がる。ログを見ても犯人が分からないコードは、プロダクションコードではない。

2. ExceptionDispatchInfo:例外の完全保存と転送

`ExceptionDispatchInfo` は、例外オブジェクトと当時のスタックトレース情報を「カプセル化」して保持するクラスだ。これを使えば、例外をキャプチャし、好きなタイミングで、まるでその場所で発生したかのように再スローできる。

実践的コード:スレッド間エラー伝播モデル

以下は、バックグラウンドタスク(DB処理やファイルI/O)で発生したエラーをメインスレッドに安全に持ち帰るためのテンプレートだ。

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

Public Class ExceptionHandlerService

”’

”’ バックグラウンド処理を実行し、例外をメインスレッドへ安全に伝播させる
”’

Public Sub ExecuteSecureTask()
Dim capturedException As ExceptionDispatchInfo = Nothing

‘ 別タスクでの処理をシミュレート
Task.Run(Sub()
Try
‘ ここでDB操作やファイル読み込みを行う
Throw New InvalidOperationException(“DB接続がタイムアウトしました。”)
Catch ex As Exception
‘ 例外情報をスタックトレースと共に保存
capturedException = ExceptionDispatchInfo.Capture(ex)
End Try
End Sub).Wait()

‘ メインスレッド側でキャプチャ済み例外を再スロー
If capturedException IsNot Nothing Then
‘ ここで投げると、あたかもTask内で起きた例外として再スローされる
capturedException.Throw()
End If
End Sub

End Class

3. なぜこの設計が「堅牢」なのか

このアプローチには、業務自動化エンジニアとして押さえておくべき「3つの優位性」がある。

1. スタックトレースの保持: `Throw` した場所ではなく、実際に例外が発生した行番号まで正確にデバッガが追跡できる。
2. スレッド境界の超越: `Task` や `Thread` を跨いでも例外の「文脈」が失われない。UIスレッドへの通知やログ出力において、極めて強力な武器となる。
3. 保守性の向上: エラーハンドリングのロジックを共通化できる。`Capture` する場所と `Throw` する場所を分離することで、エラーログのフォーマットを一元管理できるからだ。

4. 業務自動化における注意点

この技術は強力だが、銀の弾丸ではない。以下の運用ルールを徹底すること。

  • `Finally` ブロックとの併用: ファイルやDBのコネクションを確実に閉じるため、例外キャプチャ前後には必ず `Finally` ブロックでリソース解放を行え。`Using` ステートメントの使用は鉄則だ。
  • ログ出力は「一度だけ」: キャプチャした例外を再スローする前には、必ずログ出力(NLogやLog4Net等)を行え。再スローした先で何度もログを吐くと、ログファイルがノイズで埋め尽くされる。
  • 例外の握りつぶし厳禁: `Catch` 句で何も書かないコード(空のCatch)は、システムを「ゾンビ化」させる。最低限、`ExceptionDispatchInfo` でのキャプチャと再スローを義務付けろ。

結論

Visual Basicという言語は、古臭いのではない。使い方次第で、これほどまでに洗練されたエラーハンドリングを実現できる現代的なツールだ。

「エラーを捕まえて、投げる」という単純な動作一つとっても、そこに込められた設計意図がプロダクトの寿命を決める。次に君が書くコードでは、ぜひこの `ExceptionDispatchInfo` を使い、完璧なトレーサビリティを備えた自動化ツールを実現してほしい。

現場の混乱を未然に防ぐことこそ、真のエンジニアリングである。

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