関数の中でエラーを丁寧に扱うと、「原因の特定」「呼び出し側への伝え方」「後処理(片付け)」がうまく回ります。初心者向けに、よく使うパターンとサンプルをまとめます。
基本パターン(On Error GoTo + Err オブジェクト)
構造の定型
Function SafeDivide(ByVal a As Double, ByVal b As Double) As Variant
On Error GoTo HandleError
' 入力の事前チェック(論理エラーの予防)
If b = 0 Then
Err.Raise Number:=vbObjectError + 1000, _
Source:="SafeDivide", _
Description:="ゼロ除算はできません"
End If
SafeDivide = a / b
Exit Function
HandleError:
' ここに来たら例外が起きている
SafeDivide = CVErr(xlErrDiv0) ' 返り値でエラーを示す(セル関数互換)
' ログに残したい場合は Debug.Print など
Debug.Print "Error(" & Err.Number & "): " & Err.Description
' Err.Clear は次の処理に備えてクリアする時のみ
End Function
VB- On Error GoTo: 例外が起きたら指定ラベルへジャンプさせる基本形。
- Err.Raise: 意図的に「使い方が不正」などの論理エラーを発生させ、ハンドラへ流す。
- Exit Function: 正常系の最後で必ず抜ける。
- HandleError ラベル: 返り値の決め方、ログ、後始末を一箇所に集約。
返し方の設計(どの形で伝える?)
- Variantでセルエラーを返す:
- 例:
CVErr(xlErrValue),CVErr(xlErrDiv0) - セルから呼ぶ関数や、呼び出し側が「エラー値」を扱いたい時に向く。
- 例:
- Boolean+ByRefメッセージを返す:
Function TryParseInt(ByVal s As String, ByRef value As Long, ByRef message As String) As Boolean
On Error GoTo HandleError
value = CLng(s)
TryParseInt = True
Exit Function
HandleError:
TryParseInt = False
message = "整数に変換できません: " & s
End Function
VB- 成否をBooleanで、詳細はメッセージで渡す。手続き的な呼び出しに向く。
- 成功時は値、失敗時はNull/Nothing/Empty:
- Variantなら
Empty/Nullを使える。配列やオブジェクトならNothing。
- Variantなら
On Error Resume Next の安全な使い方
部分的に失敗を許容して進めたい場合だけ、範囲を限定して使います。
Function SheetExists(ByVal name As String) As Boolean
Dim ws As Worksheet
On Error Resume Next
Set ws = ThisWorkbook.Worksheets(name)
If Err.Number <> 0 Then
Err.Clear
SheetExists = False
Else
SheetExists = True
End If
On Error GoTo 0 ' 既定のエラーハンドリングに戻す
End Function
VB- ポイント:
- Resume Next の直後の操作だけを対象にする。
- チェック後は
Err.ClearとOn Error GoTo 0で元に戻す。 - だらだら長い範囲での使用はバグの温床。
クリーンアップ(擬似 finally)
ファイル/オブジェクト/配列などの後始末を、正常・異常どちらでも確実に実行します。
Function ReadFirstLine(ByVal path As String) As Variant
Dim ff As Integer
Dim line As String
On Error GoTo HandleError
ff = FreeFile
Open path For Input As #ff
Line Input #ff, line
ReadFirstLine = line
GoTo CleanExit
HandleError:
ReadFirstLine = CVErr(xlErrNA)
Debug.Print "Read error: "; Err.Number; Err.Description
CleanExit:
If ff <> 0 Then
Close #ff
End If
End Function
VB- ラベルを分ける: HandleError と CleanExit を分離すると読みやすい。
- 状態フラグ: 開いているかどうかを判定して後始末。
入力バリデーションと早期リターン
「エラーになりうる入力」を先回りで弾くと、ランタイム例外を減らせます。
Function GetPrimesInRange(ByVal startNum As Long, ByVal endNum As Long) As Variant
On Error GoTo HandleError
If startNum < 2 Then startNum = 2
If endNum < startNum Then
Err.Raise vbObjectError + 1001, "GetPrimesInRange", "範囲が不正です"
End If
' ... 素数計算(省略) ...
Exit Function
HandleError:
GetPrimesInRange = CVErr(xlErrValue)
End Function
VB- 先に範囲チェック: 条件が不正なら計算を始める前に落とす。
- メッセージを具体的に: 呼び出し側が直しやすくなる。
カスタムエラー番号とログ
- 番号設計:
vbObjectError + 1000のように、自分の領域を決めて重複を避ける。 - Source に関数名: どこで起きたか追跡しやすい。
- ログの出力:
Debug.Print,Trace, あるいは専用のロギング関数に集約。
Sub LogErr(ByVal proc As String, ByVal e As ErrObject)
Debug.Print "[" & proc & "] Err " & e.Number & ": " & e.Description
End Sub
VBよくある落とし穴と回避
- Resume Next の使い過ぎ: 例外を見えなくする。短いスコープで限定使用。
- Err.Clear の乱用: 原因追跡が困難に。必要な場面だけ。
- 返り値が曖昧: 何が「成功」かを返り値で明確化。仕様を決める。
- 後始末忘れ: ファイル/オブジェクトは必ずクリーンアップラベルへ。
まとめの指針
- 事前に弾く(入力チェック) → 失敗を検知(Err/ハンドラ) → 呼び出し側へ分かる形で返す(値・コード・メッセージ) → 必ず片付ける(CleanExit)
この流れをひとつのテンプレにしておくと、どの関数でも安定して扱えます。
