知らずに落ちると抜け出せなくなるVBAの落とし穴です。
- 入るはずがないIfステートメントの中に入ってしまう。
- スキップした気でいた処理が実行されてしまう。
あたりにお悩みの方は、この落とし穴に落ちていないかご確認ください。
落とし穴にハマったサンプル
例えばこんなマクロで罠が発生します。
Sub 空のセルを赤くする(指定エリア As Range) On Error Resume Next Dim セル As Range For Each セル In 指定エリア If セル.Value = "" Then セル.Interior.Color = RGB(255, 0, 0) End If Next End Sub
実行結果
空でないセルまで赤くなってるやつがいますね…。
解説
ひとまず基礎知識ですが、
「#エラー値」を他の値と比較しようとすると、
「型が一致しません」エラーを返します。
「1 > "りんご"」をやろうとすると出てしまうおなじみのエラーですが、
#エラー値の場合は「>」だけでなく、「=」で比較するのもダメなのです。
よって画像中の「#VALUE!のセル」が""かどうかの判定でエラーが発生します。
これを安易にOn Error Resume Nextを使ってスキップしようとしましたが、タイトルの通り、
Ifの判定部分がOn Error Resume Nextでスキップされた場合、
Ifステートメントは「TRUE」側に分岐します。
そのせいで空でないセルが赤くなってしまいました。
こんな風に錯覚してしまうのは、
- 条件式をスキップ ⇒ Booleanの初期値Falseになりそう
- Trueなら通ってよし ⇒ TrueかFalseかわからない ⇒ 通さぬ
みたいな人間の感覚からくるものと推測しますが、実際は逆になります。
ざっくりいうと、Ifステートメントは「Trueなら通ってよし」という処理ではなく、
「Falseなら(ElseかEndIfまで)ジャンプしろ」という処理なのです。
タイトルに書いておいてなんですが、TRUEに分岐という表現もあまり正しくなくて、
「EndIf までジャンプ という処理がスキップされて、普通に次の行が実行された」
と解釈したほうがより正確かもしれません。
ちなみにこういった理屈なので、
If Not (セル.Value <> "") Then
こんな風に「空じゃなくなくない?」みたいなひねくれた対処法をやっても無駄なので注意してください。
判定がTRUEになるのではなく、Ifステートメントのジャンプ自体がスキップされますので、どちらにせよ#VALUEは赤くなります。
解決策
さて解決策ですが、当然ながらOn Error Resume Nextでスキップしてしまったエラーによって解決策や対処法は変わりますので、エレガントな一発解答は残念ながらありません。
「On Error Resume NextとIf文を安易に一緒に使わないように心がける」のが唯一の解決策です。
On Error Resume Nextを使うときはこの仕様を頭に入れておきましょう。
おまけ:今回のサンプルにおける解決策
On Error Resume Next自体の解決法ではないので本題とはちょっとそれますが、
今回題材にした
「データをループ処理中にエラー値のセルに出くわす」
事態に対処する場合は、以下のような解決策があります。
エラー値をIfで分岐する
もっとも愚直な解決法ですが、
If Not IsError(セル.Value) Then If セル.Value = "" Then セル.Interior.Color = RGB(255, 0, 0) End If End If
と、セル値がエラー値か調べてから処理する方法です。
Ifごとにこれを書くのは面倒ですが、面倒な分万能です。
IfではなくSpecialCellsで処理するセルを判定する
今回の「空セルへ処理」に限れば、
Sub 空白セルを赤くする2(指定エリア As Range) On Error Resume Next 指定エリア.SpecialCells(xlCellTypeBlanks).Interior.Color = RGB(255, 0, 0) End Sub
と、SpecialCellsで空セルを取得して処理してしまってもOKです。
ここで使ったOn Error Resume Nextは、空セルがなかった場合の、
「Nothing.Interior.Color ⇒ 該当するセルが見つかりません。」
エラーを回避しているもので、これは正しい使い方ですね。
.Textプロパティを使う
裏技的になりますが、
If セル.Text = "" Then
とやっても実は解決します。
Valueプロパティではなく、Textプロパティを使うと、
「#VALUEという文字列」が""かどうかという判定になって、ちゃんと動きます。
が、こっちを使うとセルの書式設定によってマクロの挙動が変わるようになり、
かえってまずい事態を招きかねません。
空のセル判定以外でこれを使うのはやめておきましょう。
ワークシートの方をなんとかする
VBAばっかりやってると盲目になってしまいがちですが、
「ワークシートからエラー値をなくす」のも結構大事な解決方法です。
「#DIV/0!」のエラーなんかは、マクロ側で頑張るより、
- 単価 = IF(個数=0,0,金額/個数)
と、0で割らないようにワークシートの関数を書き換えたほうが簡単です。
「だれがどんな使い方をしてもOK」という商品レベルのマクロではなく、
ただ自社で業務効率するだけのマクロであるならば
- エラー処理はせずにエラーを発生させる
- 発生したら、エラーセルを手で書き替えてマクロを再開する
これも立派な解決策ですので、プログラムばっかり書きすぎて、視野が狭くならないように気をつけましょう。