【テクニカル・上級編】VB.NETでのカスタム例外の作成とスロー:業務エラーを明確に区別する例外設計の極意 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

業務例外の設計は「死活管理」である:VB.NETにおけるカスタム例外の極意

VBAの泥沼からVB.NETへと移行し、数々のエンタープライズシステムを構築してきた諸君へ。
「とりあえず `Exception` を投げておけば動く」という考えは、今すぐ捨てろ。

システム開発において、最もコストを支払うのは「エラーが起きた時」ではない。「エラーの原因を特定する時」だ。特にレガシーなWindows API呼び出しが混在する環境や、複雑なステートフルな業務ロジックにおいて、システムエラー(予期せぬ例外)と業務エラー(想定内の入力不備)を区別しないコードは、後進への呪いとなる。

今日は、プロフェッショナルとしてシステムを掌握するための、カスタム例外実装の「真髄」を授ける。

—

1. なぜ「Exceptionクラス」を直投げしてはいけないのか

標準の `System.Exception` は、あくまで「汎用的な容器」に過ぎない。これをそのまま使うことは、現場のトリアージを放棄することと同義だ。

  • 型による識別不能: `catch (ex As Exception)` では、ネットワーク遮断によるDB接続エラーと、ユーザーの入力値不備(桁数オーバー等)を同列に扱うことになる。
  • ログ解析の汚染: 業務エラーはログにおいて「通知」であるべきであり、「障害」ではない。これらを混同すると、監視運用におけるアラートの誤検知が増大し、真に重要な異常が見逃される。

—

2. 業務例外の設計:継承階層の構築

カスタム例外を実装する際は、必ず「基底業務例外」を一つ定義し、それを継承する形をとれ。これにより、上位層でのハンドリングが劇的に簡潔になる。

.net
”’

”’ 業務ロジック内で発生するすべての例外の基底クラス
”’

Public Class BusinessBaseException
Inherits ApplicationException ‘ .NET Core以降でも明確な区別のために使用

Public ReadOnly Property ErrorCode As String

Public Sub New(message As String, errorCode As String)
MyBase.New(message)
Me.ErrorCode = errorCode
End Sub
End Class

”’

”’ 具体的な入力不備例外
”’

Public Class InvalidInputException
Inherits BusinessBaseException

Public Sub New(message As String)
‘ 業務エラーコードは定数クラスで管理せよ
MyBase.New(message, “ERR-INPUT-001”)
End Sub
End Class

—

3. レガシーAPI呼び出しとの共存:リソースの安全な解放

Windows APIをP/Invokeで呼び出す場合、例外が発生した瞬間にメモリリークやハンドル開放漏れが起きることがある。`Try-Catch` ブロックだけでなく、`Finally` を強制する設計が必須だ。

特に `IDisposable` を実装していないCOMオブジェクトやネイティブハンドルを扱う際は、「例外発生時こそ、即座にリソースを解放する」という鉄の掟を守れ。

.net
Public Sub ExecuteNativeOperation()
Dim handle As IntPtr = IntPtr.Zero
Try
handle = NativeMethods.OpenFile(“data.dat”)
If handle = IntPtr.Zero Then
‘ 業務例外として上位に通知
Throw New InvalidInputException(“指定されたファイルが開けません。”)
End If
‘ 処理ロジック…
Finally
‘ 例外が起きても必ず解放を保証する
If handle <> IntPtr.Zero Then
NativeMethods.CloseHandle(handle)
End If
End Try
End Sub

—

4. 呼び出し側のハンドリング:システムと業務を切り離す

呼び出し側(UI層やコントローラー層)では、以下の順序でキャッチせよ。

1. 業務例外: ユーザーにメッセージを提示し、処理を継続可能にする。
2. システム例外: 致命的なエラーとしてログに詳細を書き出し、セッションを破棄する。

.net
Try
‘ 業務ロジック呼び出し
MyService.Process()
Catch ex As BusinessBaseException
‘ 業務エラー:ユーザーに優しく伝える
MessageBox.Show(ex.Message, “業務エラー”, MessageBoxButtons.OK, MessageBoxIcon.Warning)
Catch ex As Exception
‘ システムエラー:ログを出力し、管理者へ通知
Logger.Fatal(ex, “予期せぬ致命的なエラーが発生しました。”)
ShowEmergencyDialog()
Finally
‘ 必要に応じてグローバルなオブジェクトのクリーンアップ
End Try

—

伝説的アーキテクトからの助言

  • シリアライズを忘れるな: ネットワーク越しに例外を飛ばす可能性があるなら、カスタム例外クラスには `` 属性を付与し、ストリームに対応させろ。
  • メッセージの抽象化: 例外メッセージの中に「DBのテーブル名」や「SQLの断片」を直接書くな。それはセキュリティリスクであると同時に、保守性を著しく下げる。エラーコードを吐き出し、詳細はログファイルに隔離せよ。
  • パフォーマンスへの配慮: 例外は重い。`Throw` は制御フローではなく、あくまで「異常事態」のためにある。フロー制御に例外を使うなど言語道断だ。

君たちが書くその一行が、数年後のメンテナンス担当者の夜を救うか、あるいは地獄に突き落とすか。技術者としての矜持をコードに刻め。以上だ。

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