[VBA] On Error Resume Next で条件判定の次の行

2017年2月27日月曜日

VBA

VBAではエラーが発生しても、処理を継続させるための構文として On Error Resume Next が用意されている。
これを使えば、エラー発生時にも処理位置が次の行に移って、処理が継続される。

Visual Basic言語リファレンス On Error ステートメント
On Error Resume Next は、実行時エラーを発生させたステートメントの直後のステートメント、または、On Error Resume Next ステートメントを含むプロシージャの最新の呼び出しに続くステートメントで、実行を続行します。

うん、まあだいたいは分かるけど、If文の判定条件でエラーが発生したりすると、制御はどこに移動するんだろう?

わざとエラーを発生させる

試しにこんな関数を用意してみよう。
Private Function RaiseError(ByVal n As Long) As Boolean
    RaiseError = True

    If n = 0 Then
        Err.raise 5
    ElseIf n < 0 Then
        RaiseError = False
    End If
End Function
nが正の場合はTrueを返して、nが負の場合はFalseを返す。nが0の場合はエラーを発生させる。

Ifの場合

こんな感じで呼べば、判定条件が True And False になるので、If文の中には到達しない。
    On Error Resume Next

    If RaiseError(1) And RaiseError(-1) Then
        Debug.Print "到達不能..."
    End If
じゃあ、これはどうだろう。
    On Error Resume Next

    If RaiseError(1) And RaiseError(-1) And RaiseError(0) Then
        Debug.Print "なぜか到達..."
    Else
        Debug.Print "到達しない."
    End If
これを実行すると、"なぜか到達..."が出力される。
判定条件がTrueと解釈されるのだろうか?

うーん、ループも試してみよう。

ループの場合

    On Error Resume Next

    Do While RaiseError(0)
        Debug.Print "無限ループ..."
    Loop
これは無限ループになる。
    On Error Resume Next

    Do Until RaiseError(0)
        Debug.Print "無限ループ..."
    Loop
これも無限ループになる。
判定条件がTrueと解釈される訳ではないようだ。
    On Error Resume Next

    Do
        Debug.Print "一度だけ実行..."
    Loop While RaiseError(0)
これは一度だけ実行される。

結局...

どうも判定条件がTrueになるとか、Falseになるとかいうことではなく、ソースコードの字面上まさに次のステートメントに制御が移るということみたい。

確かにそれを頭に入れて、リファレンスの説明を読み直すと、そういう風にも読める。
そもそもGotoが構文の意味に関わらず、指定されたラベル(行)に移動することを考えれば、上記の動作仕様も自然なものに思える。
つまり、"On Error Goto 次の行" ということだね。

でも、これが意図したエラートラップにつながるとは思えないし、On Error Resume Next を使った時は条件判定内にエラーが発生しうる処理は入れない方がよさそう。