インデントが深くなる理由
VBAに限らずどんなプログラミング言語でもよく見かけるのが深すぎるインデントです。深いインデントは見づらさだけでなく、バグの原因にもなるため、出来るだけ避けた方がよいです。
If文がどんどん深くなるのには理由があります。それは、条件文を考え付いた通りの条件で「変数Aが1の場合」のようにそのままコードを書いてしまうためです。
深すぎるインデントは、条件文の書き方を工夫すると改善することが出来ます。
もしインデントが深くなる傾向がある方で改善したい方は以下を参考にしてください。
深すぎるインデントの例
深すぎるインデントのサンプルコードです。
以下の条件を満たすセルだったらメッセージボックスを出す、という処理です。
- セルの背景色が赤色
- 値が1
- 太字
- フォントサイズが10
- セルの座標がA1
それぞれの条件をIf文で書いているため、見事に逆「く」の字型になっています。
途中に処理を追加したい場合などの対応が必要になった場合、この程度の行数でしたら修正個所が一目で分かりますが、行数が長い関数になると、どの条件のIf文なのかを確かめながら見る必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
Sub IndentSample() '// セルの背景色が赤 If ActiveCell.Interior.Color = RGB(255, 0, 0) Then '// 値が1 If ActiveCell.Value = 1 Then '// 太字 If ActiveCell.Font.Bold = True Then '// フォントサイズが10 If ActiveCell.Font.Size = 10 Then '// セルの座標がA1 If ActiveCell.Address(False, False) = "A1" Then MsgBox "条件一致", vbOKOnly End If End If End If End If End If End Sub |
では、改善の方法を以下で紹介します。
方法1. 各条件をAndでまとめてインデントを減らす
上記の書き方は5つの条件をそれぞれIf文で分けて書いているためインデントが深くなっています。
これらの条件をAnd条件として1つのIf文にまとめてしまえば、インデントは1つで済みます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Sub IndentSample1() '// セルの背景色が赤 '// 値が1 '// 太字 '// フォントサイズが10 '// セルの座標がA1 If ActiveCell.Interior.Color = RGB(255, 0, 0) And _ ActiveCell.Value = 1 And _ ActiveCell.Font.Bold = True And _ ActiveCell.Font.Size = 10 And _ ActiveCell.Address(False, False) = "A1" Then MsgBox "条件一致", vbOKOnly End If End Sub |
ただ、この方法はあまりお勧めしません。
理由は、各条件をAndでつなげて1つのIf文にまとめてしまうと、途中の条件判定で不一致とみなすことができても、全ての条件が評価されてしまうためです。
例えば1つの目の条件であるセルの背景色が赤色かどうか判定で実際のセルの背景色が白色であるためそこでメッセージボックスは表示しないと分かっていても、2番目以降の条件が一致しているかどうかの判定は無駄な処理ですが行われてしまいます。
このように無駄な判定が発生するため、高速性を求める場合には不利になります。
方法2. 否定条件を使ってインデントを無くす
最初のサンプルコードは分岐条件の5つをそれに沿ったIf文で書いているためインデントが深くなっています。
しかし、同じ条件でもインデントを使わずに書くことが出来ます。
それは、条件に一致しない場合、という書き方に変えることです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Sub IndentSample2() '// セルの背景色が赤 If ActiveCell.Interior.Color <> RGB(255, 0, 0) Then Exit Sub '// 値が1 If ActiveCell.Value <> 1 Then Exit Sub '// 太字 If ActiveCell.Font.Bold <> True Then Exit Sub '// フォントサイズが10 If ActiveCell.Font.Size <> 10 Then Exit Sub '// セルの座標がA1 If ActiveCell.Address(False, False) <> "A1" Then Exit Sub MsgBox "条件一致", vbOKOnly End Sub |
上のコードはIf文を1行にして、先の条件とは逆に、条件に一致しなければExit Subで処理を抜けるようにしています。
重要なのは、指定された条件の反対の条件であれば処理対象外になる、という点です。
なお、否定条件にしてOr文でつなげる方法もありますが、これはAnd条件の場合と同様に各条件が評価されるため高速性が不利になります。
C言語などの場合はOr条件の途中で評価を止めて以降の条件判定をしないようにできますが、VBAは言語仕様としてAndだけでなくOrで結ばれた場合も全ての条件が評価されてしまいます。
方法3. 条件をまとめた関数を用意する
インデントが多くなる原因にはもう一つあります。それは、関数内のコード量が多い、ということです。
例外はありますが、1つの関数の行数は100行以内、できれば50行程度に収めるように書くようにすると、すっきりしますしバグも減ります。1つの関数の中に処理を詰め込む書き方をするとどうしてもごちゃごちゃしてしまいます。
そういう場合は処理の一部を関数に切り出して、それを呼び出すように変えた方が見た目も保守性もよくなります。
以下はIf文の条件を1つの関数CheckConditionにまとめて、それを呼び出すように変えたものです。CheckCondition関数は全ての条件に一致する場合はTrueを返し、1つでも条件に一致しない場合はFalseを返します。CheckCondition関数での各条件の考え方は方法2の条件の不一致の考え方と同じです。
関数を分けることでコード量は増えていますが、それぞれの役割がはっきり分かれたため、保守性は極めて高くなります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
Sub IndentSample3() '// 条件に一致する場合 If CheckCondition = True Then MsgBox "条件一致", vbOKOnly End If End Sub '// 途中の条件判定で不一致とみなされた場合はそこでFalseを返す Function CheckCondition() '// セルの背景色が赤 If ActiveCell.Interior.Color <> RGB(255, 0, 0) Then CheckCondition = False Exit Function Exit Function '// 値が1 If ActiveCell.Value <> 1 Then CheckCondition = False Exit Function Exit Function '// 太字 If ActiveCell.Font.Bold <> True Then CheckCondition = False Exit Function Exit Function '// フォントサイズが10 If ActiveCell.Font.Size <> 10 Then CheckCondition = False Exit Function Exit Function '// セルの座標がA1 If ActiveCell.Address(False, False) <> "A1" Then CheckCondition = False Exit Function Exit Function '// 条件一致 CheckCondition = True End Function |
まとめ
深いインデントを解消するには以下を検討するようにしてください。
- 関数化を行う。
- 条件に一致しているIf文ではなく、条件に不一致のIf文を検討する。
- 複数の条件をAndやOrでまとめる。
特に、条件に不一致、という書き方は今まで「条件に一致する」If文を書いてきた方にとっては最初は慣れない点もあるとは思います。
しかし、慣れるのにそんなに時間は掛かりませんし、一度慣れてしまえばコーディングの幅が広がりますので、是非習得してみてください。