以下のプログラムを実行すると、ランダムな数値が出力される。
Sub BugSample() Dim Arr(1 To 10) As Long Dim UpperBound As Long: Upper = 1000 Dim LowerBound As Long: Lower = 10 Dim i As Integer For i = 1 To 10 Arr(i) = Int((UpperBound - LowerBound + 1) * Rnd + LowerBound) Next Dim j As Integer For j = 1 To 10 Debug.Print Arr(j) Next End Sub
…嘘である。
わざとらしいバグなので一見してすぐに気付いた人もいるかもしれないが、これを実行してもイミディエイトウインドウには0が10個並ぶだけである。
バグは変数名である。UpperBoundとして変数宣言しているのに、実際にはUpperに代入している。LowerBoundも同様だ。
こういうバグが生まれるのは、コーディングの途中で変数名が分かりづらいことに気づき、直したはいいが一部直し忘れたようなケースである。Option Explicitである程度防ぐことはできるものの、実務のコードは複雑で、必ずしもそれで万全とは言えない。
実際にはこのように間に処理がはさまっていて、Upper、Lowerもちゃんと宣言されて別の用途で使われているケースもある。すると、Option Explicitには引っかからず、バグの原因に頭を悩ませることになる。
Option Explicit Sub BugSample() '~処理×20行~ Dim Upper As Long Dim Lower As Long '~処理×30行~ Dim Arr(1 To 10) As Long Dim UpperBound As Long: Upper = 1000 Dim LowerBound As Long: Lower = 10 '~処理×10行~ Dim i As Integer For i = 1 To 10 Arr(i) = Int((UpperBound - LowerBound + 1) * Rnd + LowerBound) Next '~処理×10行~ Dim j As Integer For j = 1 To 10 Debug.Print Arr(j) Next '~処理×5行~ End Sub
そこで、For文の中に、Debug.Assertを埋め込んでおく。
For i = 1 To 10 Debug.Assert LowerBound >= 10 Debug.Assert UpperBound <= 1000 Arr(i) = Int((UpperBound - LowerBound + 1) * Rnd + LowerBound) Next
Debug.Assertは、「こうなるはず」という条件を指定しておき、そうならなかった場合にプログラムを中断させる機能である。
LowerBound変数は10以上のはず→Debug.Assert LowerBound >= 10
UpperBound変数は1000未満のはず→Debug.Assert UpperBound <= 1000
実行すると、プログラムは条件の不成立を発見し、停止する。
If文とStop文の組み合わせでも、似たようなことはできる。
しかし、If文は単に「条件を満たしたら停止するんだな」という以上の意味を持たない。
Debug.Assertの方は「こうなるはず」というプログラマの考えを読み取ることができる。
また、前回紹介したウォッチウインドウでも条件の監視はできる。
thom.hateblo.jp
ただし、ウォッチウインドウではプログラム全体を通して条件成立を監視することはできるが、コードの特定個所での条件成立をピンポイントで監視することはできない。
Debug.Assertはコードの特定の時点での条件不成立を監視する。ウォッチウインドウとは逆である。ただ、Notを付ければ反転できるので条件指定の機能は同等と考えて良い。
異常を前もって知らせるためにDebug.Assertは便利である。
プログラムが完成したら、保守に備えてコメントアウトしておくと良い。