サーチ…
エラー状態の回避
ランタイムエラーが発生した場合、良いコードがそれを処理する必要があります。最適なエラー処理戦略は、エラー状態をチェックし、実行時エラーを引き起こすコードの実行を単純に回避するコードを記述することです。
ランタイムエラーを減らすための重要な要素の1つは、小さな手続きを書くことです。手続きが失敗する理由が少なくなればなるほど、コード全体がデバッグされやすくなります。
実行時エラーの回避91 - ObjectまたはWithブロック変数が設定されていない:
このエラーは、参照が割り当てられる前にオブジェクトが使用されているときに発生します。オブジェクトパラメータを受け取るプロシージャがあるかもしれません:
Private Sub DoSomething(ByVal target As Worksheet)
Debug.Print target.Name
End Sub
target
参照が割り当てられていない場合、上記のコードでは、オブジェクトに実際のオブジェクト参照が含まれているかどうかをチェックすることで、簡単に回避できるエラーが発生します。
Private Sub DoSomething(ByVal target As Worksheet)
If target Is Nothing Then Exit Sub
Debug.Print target.Name
End Sub
target
リファレンスが割り当てられていない場合、未割り当てリファレンスは使用されず、エラーは発生しません。
1つ以上のパラメータが有効でないときにプロシージャを早期に終了するこの方法は、 ガード句と呼ばれます。
実行時エラーの回避9 - 添え字が範囲外です:
このエラーは、配列が境界外でアクセスされた場合に発生します。
Private Sub DoSomething(ByVal index As Integer)
Debug.Print ActiveWorkbook.Worksheets(index)
End Sub
ActiveWorkbook
内のワークシートの数よりも大きいインデックスが与えられている場合、上記のコードは実行時エラーを発生させます。簡単なガード節では、次のことを回避できます。
Private Sub DoSomething(ByVal index As Integer)
If index > ActiveWorkbook.Worksheets.Count Or index <= 0 Then Exit Sub
Debug.Print ActiveWorkbook.Worksheets(index)
End Sub
ほとんどのランタイムエラーは、使用する前に使用している値を慎重に検証し、単純なIf
文を使用して別の実行パスで分岐することで回避できます。大規模な手順の本体。
エラー時
ガード句を使用しても、プロシージャの本体で発生する可能性のあるすべてのエラー条件を現実的に常に説明することはできません。 On Error GoTo
ステートメントは、実行時に予期しないエラーが発生するたびにVBAにラインラベルにジャンプし、「エラー処理モード」と入力するよう指示します。エラーを処理した後、コードが使用する「通常」の実行に戻って再開できるResume
キーワードを。
行のラベルはサブルーチンを表します:サブルーチンは従来のBASICコードに由来し、 GoTo
とGoSub
ジャンプとReturn
ステートメントを使用して「main」ルーチンにジャンプするため、厳密に構造化されていない場合は難しいスパゲッティコードを記述するのはかなり簡単です。このような理由から、
- プロシージャには1つのみのエラー処理サブルーチンがあります
- エラー処理サブルーチンはエラー状態でしか実行されません
これは、そのエラーを処理するプロシージャで、次のように構成する必要があります。
Private Sub DoSomething()
On Error GoTo CleanFail
'procedure code here
CleanExit:
'cleanup code here
Exit Sub
CleanFail:
'error-handling code here
Resume CleanExit
End Sub
エラー処理戦略
時には、異なるアクションでさまざまなエラーを処理したいことがあります。その場合、発生したエラーに関する情報を含むグローバルなErr
オブジェクトを調べ、それに応じて動作します:
CleanExit:
Exit Sub
CleanFail:
Select Case Err.Number
Case 9
MsgBox "Specified number doesn't exist. Please try again.", vbExclamation
Resume
Case 91
'woah there, this shouldn't be happening.
Stop 'execution will break here
Resume 'hit F8 to jump to the line that raised the error
Case Else
MsgBox "An unexpected error has occurred:" & vbNewLine & Err.Description, vbCritical
Resume CleanExit
End Select
End Sub
一般的なガイドラインとして、サブルーチンや関数全体のエラー処理をオンにし、そのスコープ内で発生するすべてのエラーを処理することを検討してください。コードの小さなセクションのセクションでのみエラーを処理する必要がある場合は、同じレベルでエラー処理をオンまたはオフにします。
Private Sub DoSomething(CheckValue as Long)
If CheckValue = 0 Then
On Error GoTo ErrorHandler ' turn error handling on
' code that may result in error
On Error GoTo 0 ' turn error handling off - same level
End If
CleanExit:
Exit Sub
ErrorHandler:
' error handling code here
' do not turn off error handling here
Resume
End Sub
行番号
VBAは、レガシースタイル(たとえばQBASIC)の行番号をサポートします。 Erl
hiddenプロパティは、最後のエラーを発生させた行番号を識別するために使用できます。行番号を使用していない場合、 Erl
は0しか返しません。
Sub DoSomething()
10 On Error GoTo 50
20 Debug.Print 42 / 0
30 Exit Sub
40
50 Debug.Print "Error raised on line " & Erl ' returns 20
End Sub
あなたがではなく、一貫して、行番号を使用している場合、 Erl
エラーが発生した命令の前に、最後の行番号を返します。
Sub DoSomething()
10 On Error GoTo 50
Debug.Print 42 / 0
30 Exit Sub
50 Debug.Print "Error raised on line " & Erl 'returns 10
End Sub
Erl
はInteger
精度しか持たず、黙ってオーバーフローすることにErl
ください。これは、 整数の範囲外の行番号が間違った結果をもたらすことを意味します。
Sub DoSomething()
99997 On Error GoTo 99999
99998 Debug.Print 42 / 0
99999
Debug.Print Erl 'Prints 34462
End Sub
行番号は、エラーの原因となったステートメントほど重要ではなく、番号の付いた行はすばやく退屈なものになり、メンテナンスにはあまり役に立ちません。
キーワードを再開する
エラー処理サブルーチンは、次のいずれかを行います。
- プロシージャの最後まで実行します。この場合、呼び出しプロシージャで実行が再開されます。
-
Resume
キーワードを使用して、同じプロシージャ内で実行を再開します。
Resume
キーワードは、エラー処理サブルーチン内でのみ使用する必要があります.VBAがエラー状態にならずにResume
遭遇した場合、ランタイムエラー20「エラーなしで再開」が発生するからです。
エラー処理サブルーチンでResume
キーワードを使用するには、いくつかの方法があります。
-
Resume
のみを使用すると、エラーの原因となったステートメントで実行が継続されます 。その前にエラーが実際に処理されない場合、同じエラーが再度発生し、実行が無限ループに入る可能性があります。 -
Resume Next
は、エラーを引き起こしたステートメントの直後のステートメントで実行を続行します。その前にエラーが実際に処理されていない場合は、無効なデータが継続して実行され、論理エラーや予期しない動作が発生する可能性があります。 -
Resume [line label]
は、指定されたラインラベル (またはレガシースタイルのライン番号を使用している場合はライン番号)で実行を続けます。これは通常、呼び出し元に戻る前にデータベース接続が閉じられていることを確認するなど、プロシージャを正常に終了する前にクリーンアップコードを実行することを可能にします。
エラーの再開時に次へ
On Error
ステートメント自体はResume
キーワードを使用して、 すべてのエラーを効果的に無視するようにVBAランタイムに指示できます 。
その前にエラーが実際に処理されていない場合は、無効なデータが継続して実行され、 論理エラーや予期しない動作が発生する可能性があります 。
上記の強調は十分に強調することはできません。 On Error Resume Nextは、すべてのエラーを効果的に無視し、カーペットの下でそれらを押します 。無効な入力が与えられた場合、ランタイムエラーが発生したプログラムは、未知の/意図しないデータを使用し続けるプログラムよりも優れています。なぜなら、バグがはるかに簡単に識別できるからです。 On Error Resume Next
バグを簡単に隠すことができます。
On Error
ステートメントは、プロシージャスコープです。そのため、 通常 、そのようなOn Error
ステートメントは、特定のプロシージャ内に1つだけ存在する必要があります。
しかし、 時にはエラー状態を避けることができない場合があります。エラー処理のサブルーチンにジャンプするだけで、 Resume Next
が正しく動作しません。この特定の場合、2つのOn Error
ステートメントの間に、既知の可能性のあるステートメントをラップすることができます。
On Error Resume Next
[possibly-failing statement]
Err.Clear 'resets current error
On Error GoTo 0
On Error GoTo 0
命令は、現行プロシージャー内のエラー処理をリセットします。これにより、実行時エラーを引き起こすそれ以上の命令はそのプロシージャー内で処理されず、代わりにアクティブ・エラー・ハンドラーによってキャッチされるまでコール・スタックを渡します。呼び出しスタックにアクティブなエラーハンドラがない場合、処理されない例外として扱われます。
Public Sub Caller()
On Error GoTo Handler
Callee
Exit Sub
Handler:
Debug.Print "Error " & Err.Number & " in Caller."
End Sub
Public Sub Callee()
On Error GoTo Handler
Err.Raise 1 'This will be handled by the Callee handler.
On Error GoTo 0 'After this statement, errors are passed up the stack.
Err.Raise 2 'This will be handled by the Caller handler.
Exit Sub
Handler:
Debug.Print "Error " & Err.Number & " in Callee."
Resume Next
End Sub
カスタムエラー
特殊なクラスを書くときには、独自のエラーを発生させたいと思うでしょうし、これらのカスタムエラーを処理するためのユーザ/呼び出しコードのためのきれいな方法が必要になります。これを達成するためのきちんとした方法は、専用のEnum
型を定義することです。
Option Explicit
Public Enum FoobarError
Err_FooWasNotBarred = vbObjectError + 1024
Err_BarNotInitialized
Err_SomethingElseHappened
End Enum
vbObjectError
ビルトイン定数を使用すると、カスタムエラーコードが予約済み/既存のエラーコードと重複しないようにできます。最初の列挙値のみを明示的に指定する必要があります。各Enum
メンバーの基になる値は前のメンバーより1
大きいため、 Err_BarNotInitialized
の基本値は暗黙的にvbObjectError + 1025
です。
独自のランタイムエラーを発生させる
Err.Raise
文を使用して実行時エラーを発生させることができるため、カスタムErr_FooWasNotBarred
エラーは次のように発生させることができます。
Err.Raise Err_FooWasNotBarred
Err.Raise
メソッドでは、カスタムのDescription
とSource
パラメータもErr.Raise
できます。このため、各カスタムエラーの説明を保持する定数を定義することをお勧めします。
Private Const Msg_FooWasNotBarred As String = "The foo was not barred."
Private Const Msg_BarNotInitialized As String = "The bar was not initialized."
そして、各エラーを発生させる専用のプライベートメソッドを作成します。
Private Sub OnFooWasNotBarredError(ByVal source As String)
Err.Raise Err_FooWasNotBarred, source, Msg_FooWasNotBarred
End Sub
Private Sub OnBarNotInitializedError(ByVal source As String)
Err.Raise Err_BarNotInitialized, source, Msg_BarNotInitialized
End Sub
クラスの実装では、これらの特殊なプロシージャをコールしてエラーを発生させることができます。
Public Sub DoSomething()
'raises the custom 'BarNotInitialized' error with "DoSomething" as the source:
If Me.Bar Is Nothing Then OnBarNotInitializedError "DoSomething"
'...
End Sub
クライアントコードは、それ自身のエラー処理サブルーチンの中で、他のエラーと同様にErr_BarNotInitialized
を処理できます。
注:従来のError
キーワードはErr.Raise
代わりに使用することもできますが、廃止または廃止されました。