VBAには三項演算子はないが代替関数がある

VBAにも他の言語と同様に三項演算子があります。正確には演算子ではなくIIf関数で実現しています。このIIf関数ですが認知度がとても低いようです。

その理由の1つに書籍に書いてあることがほとんど無い、というのがあると思われます。では、なぜ書籍には書かれていないのかと言うと、想定外の動作をするという考え方によってはひどいバグが実装されているためではないか思われます。

その想定外の動作の詳細は後述しています。

他の言語であれば普通は三項演算子について本に書いてありますからね。書いていないということは想定外の動作が原因と考えてよいと思います。


構文

Function IIf(Expression, TruePart, FalsePart)

Expression Boolean型の結果を返す条件文を指定します。
TruePart Expressionの結果がTrueの場合にIIf関数の戻り値になります。

ただ、Expressionの結果に関わらず、TruePartのコードが実行されます。関数やクラスメソッドを設定することも可能ですが悲惨な目に遭う恐れがあります。(後述)

FalsePart Expressionの結果がFalseの場合にIIf関数の戻り値になります。

ただ、TruePartと同様でExpressionの結果に関わらず、FalsePartのコードが実行されます。関数やクラスメソッドを設定することも可能ですが悲惨な目に遭う恐れがあります。(後述)



三項演算子(IIf関数)の書き方

以下のコードは通常のIf文と、同じ内容を三項演算子のIIf関数で書いたコードです。

通常のIf文のコード

出力結果

条件に一致
条件に不一致

三項演算子(IIf関数)のコード

出力結果

条件に一致
条件に不一致


三項演算子(IIf関数)のバグ?(厳密にはバグではないけど・・)

VBAの三項演算子(IIf関数)には以下のページに説明があります。

https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/iif-function

上記ページの注釈に
「IIf では、 truepart または falsepart のいずれか一方だけが返されますが、評価は両方の引数に対して行われます。 このため、予期しない結果が起きることがあります。 たとえば、 falsepart を評価した結果 0 による除算エラーが発生する場合は、 expr が True であってもエラーが発生します。」
とあります。

これを読んで問題があるとすぐに気が付く人はコーディングに慣れている人です。そうでなければ、なかなか問題に気が付かないと思われます。というより、文章自体がなんのこっちゃ分からない人も少なくないでしょう。

以下は問題が発生するソースです。

出力結果
0(0 > 1の条件に一致しないため出力してほしくないが出力される)
1

これの何が問題なのかというと、IIf関数の第二引数と第三引数が関数の呼び出しなどの演算処理が行われる形式になっている場合、第一引数である条件文の結果に関わらず、第二引数と第三引数の両方が実行されてしまいます。上のコードであれば0 > 1の条件に一致するのは第三引数のため、本来であれば第三引数の「testMethond(1)」だけが処理されればいいのですが、第二引数の「testMethod(0)」も処理されてしまいます。

条件文の結果に関わらず、第二引数と第三引数が処理されています。

これは、IIfが関数であるため、すべての引数をIIf関数の処理を行うときに取り込んでしまわないと動作できないことが原因です。

三項演算子の代わりとして使ってこのことに気が付くと、「一体何のための条件文なんなの?」「バグやろ」と思ってしまうのは仕方のないことです。

結論。IIf関数は使わず、If文書きましょう。

他の言語もそうですが、やはり三項演算子(IIf関数)はおすすめしません。

どうしてもIIf関数を使うのであれば、第二引数と第三引数には関数やクラスメソッドのような内部処理が発生するものは渡さず、単純なTrueやFalseを返す程度にすべきです。しかしその程度であれば通常のIf文で書いた方が安全です。

VBAのIIfに至っては認知度はかなり低いと思われるため他人がメンテすることがあれば大変ですし、直観的にわかりにくいため、誤読やコーディングミスにつながる恐れがあります。

厳密に言えば「関数」の特性上、この挙動は仕方がないためバグではありませんが、バグと言われても仕方のない動作です。こういう関数を使ったコードは世に出すべきではありませんので、IIf関数を使うのはやめて、If文で書きましょう。