VBAにおける制御ステートメントというと、If 〜 Then 〜 Else ステートメント、Select Case ステートメントがある。書き方としては次のとおり。
'If 〜 Then 〜 Else ステートメント
Sub IfThenElse()
Dim digits As Long : digits = 3
Dim msg As String
If digits = 1 Then
msg = "一桁です。"
Else
msg = "二桁以上です。"
End if
End Sub
'Select Case ステートメント
Sub SelectCase()
Dim rank As String: rank = "優"
Dim msg As String
Select Case rank
Case "優"
msg = "すごいですね!"
Case "良"
msg = "頑張りましたね"
Case "可"
msg = "ギリギリでしたね"
End Select
End Sub
これはこれで読みやすいができれば簡単なものは一行にまとめてしまいたい。RubyとかではなんでもワンライナーにすることができるがVBAではそういうニーズには答えるようなものはないと思いこんでいた。
ところが、そういうワンライナー化のニーズに答える関数がVBAの標準ライブラリに存在した。それが、Interaction
モジュールのIIf(Expression, TruePart, FalsePart)
とSwitch(expr-1, value-1, [, expr-2, value-2, ・・・])
という関数である。
ざっと書くと、上の制御ステートメントで記述したプログラムは次のような形に置き換えることができる。
Sub IIfFunction()
Dim digits As Long : digits = 3
Dim msg As String
msg = Interaction.IIf(digits = 1, "一桁です。", "二桁以上です。")
End Sub
Sub SwitchFunction()
Dim rank As String: rank = "優"
Dim msg As String
msg = Interaction.Switch(rank = "優", "すごいですね!", rank = "良", "頑張りましたね", rank = "可", "ギリギリでしたね")
End Sub
非常に記述がスッキリした形にすることができる。短い条件分岐であれば使用する価値はある。問題は、これは条件分岐のステートメントではなく関数であるので、引数に関数を入れた場合、全ての引数の関数を評価してしまうという弱点がある。
例を出して説明しよう。
Function Hello()
MsgBox "Say Hello"
Hello = "Hello"
End Function
Function World()
MsgBox "Say World"
World = "World"
End Function
Sub SideEffect()
Dim msg As String
msg = Interaction.IIf(True, Hello(), World())
End Sub
Say Helloという文字をメッセージボックスに表示させたあと"Hello"という文字列を返す関数Hello
と同じくSay Worldという文字をメッセージボックスに表示させた後に"World"という文字列を返す関数World
を事前に定義しておく。
IIfで条件分岐させるステートメントとしては、
Interaction.IIf(True, Hello(), World())
という常にTrueパートの部分のみを返す条件分岐となっている。期待される動作としては、これを実行すれば、メッセージボックスに"Say Hello"と出たあとに"Hello"という文字が返り、msg変数にその"Hello"という文字列が格納される、というものである。ところが、これは実際の動作とは異なる。実際に実行してみると、TrueパートのHello関数が評価されるところまでは正しいが、FalseパートのWorld関数も評価されて"Say Hello"とメッセージボックスに表示された後、"Say World"というメッセージボックスが条件がTrueであるかFalseであるかに関わらず表示されてしまうのである。
つまり、引数に入れた関数が内容として副作用を伴うものであると、条件がTrueかFalseであるかに関わらず副作用が評価されてしまうのである。なお、普通のIf 〜 Then 〜 Elseステートメントであればこういうことにならない。
以上を踏まえて、機械的にIf 〜 Then 〜 Elseステートメント、Select Casedステートメントを一行関数ステートメントに置き換えるのはおすすめできないが、使い方によっては、今までのVBAプログラムが結構スッキリと簡略化できるでしょう。