VBAで配列が空かどうかを IsEmpty で判定すると、実行時エラー9「インデックスが有効範囲にありません」で止まることがあります。実務ツールの改修で実際に直したバグです。原因と、動的配列が空(未割当)かを安全に判定する方法をまとめます。
困った場面:エラー0件の日だけマクロが止まる
取込データをチェックし、問題のある行を配列にためて、最後にエラーリストとして出力する処理です。
Private errList() As Variant 'エラー記録用。エラーが出たときだけReDimされる
Sub データチェック()
'(中略)チェック処理。エラーがあれば errList に追記する
'エラーがあればリスト出力
If Not IsEmpty(errList(1, 1)) Then '←ここで実行時エラー9
Call エラーリスト出力
End If
End Sub
テストではエラーデータを混ぜるため、問題なく動きます。ところが、本番でエラーが1件もないデータが来ると止まります。


解決コード:配列が空(未割当)かを (Not 配列) = -1 で判定する
判定を次のように書き換えます。
If (Not errList) = -1 Then
'未割当 = 一度もReDimされていない = エラー0件
MsgBox "エラーはありませんでした。"
Else
Call エラーリスト出力
End If
中身があるときだけ処理する場合は、逆向きに書きます。
If (Not errList) <> -1 Then
Call エラーリスト出力
End If
配列のチェックをこの書き方に統一すれば、同じバグは起きません。
解説
IsEmpty(arr(1,1)) で止まる理由
IsEmpty は、Variant型の変数が未初期化(Empty)かどうかを調べる関数です(Microsoft公式リファレンス)。配列の状態は調べられません。
IsEmpty(errList(1, 1)) は、先に errList(1, 1) の値を取得します。一度も ReDim していない配列には、この要素が存在しません。そのため、IsEmpty が判定する前にエラー9が発生します。
なお、IsEmpty(errList) と配列ごと渡してもエラーにはなりませんが、常に False が返るため判定には使えません。
配列の「空」は2種類ある
| 状態 | どういうとき | arr(1,1) へのアクセス |
|---|---|---|
| 未割当 | Dim arr() のあと一度も ReDim していない |
実行時エラー9 |
| 割当済みで中身が空 | ReDim arr(1 To 10, 1 To 3) しただけ |
可能(Emptyが返る) |
IsEmpty(arr(1,1)) で判定できるのは下の状態だけです。未割当の判定には別の方法が必要です。
(Not arr) = -1 の仕組み
動的配列の変数は、内部で配列の実体のアドレスを持っています。未割当のとき、この値は 0 です。Not はビット反転なので、Not 0 は -1 になります。つまり、Not arr が -1 なら未割当です。
カッコは必須です。VBAは比較演算子を Not より先に評価します。Not errList = -1 と書くと Not (errList = -1) と解釈され、コンパイルエラー「型が一致しません」になります。

使うときの注意点
- 対象は
Dim arr() As Variantのようにカッコ付きで宣言した動的配列だけです。Dim arr As VariantにRange.Valueを入れる場合は、IsEmpty(arr)やIsArray(arr)で判定できます。 Split("")の戻り値のように、割当済みで要素数がゼロの配列は未割当と判定されません。- モジュールレベル変数の配列には、前回実行時の中身が残ることがあります。処理の最初に
Erase errListで未割当に戻すと安全です。 - これは公式に案内されている書き方ではなく、内部動作を利用したテクニックです。将来も同じ動作をする保証はありません。
最後の点が気になる場合は、関数にする方法もあります。
Public Function IsArrayAllocated(ByRef arr As Variant) As Boolean
On Error GoTo notAllocated
If Not IsArray(arr) Then Exit Function
IsArrayAllocated = (UBound(arr) >= LBound(arr))
Exit Function
notAllocated:
IsArrayAllocated = False
End Function
未割当の配列に UBound を使うとエラーになる性質を利用しています。要素数ゼロの配列も False になります。
AIにコードを書かせるときの注意
生成AIにVBAを書かせると、配列の空チェックに IsEmpty(arr(1,1)) を提案されることがあります。データがある前提のテストでは動くため、レビューでも見落としがちです。「この配列が一度も ReDim されない場合はないか」を確認してください。
配列にデータを足していく処理では、2次元配列ならではの制約もあります。【VBA】ReDim Preserveで2次元配列の行が増やせない原因と対処で紹介しています。
まとめ
IsEmpty(arr(1,1))は、配列が未割当だと判定の前にエラー9で止まる- 配列の「空」には「未割当」と「割当済みで中身が空」の2種類がある
- 未割当の判定は
(Not arr) = -1に統一する。カッコは必須 - 公式の書き方にこだわる場合は、
UBoundとエラートラップを使った関数で代用できる
データがあるときは動き、ないときだけ止まるバグは、本番でしか表面化しません。配列を使うマクロでは、0件のケースを必ず一度テストしてください。

コメント