VB.NETでも3項演算子を使いたい

はじめに

最近はVBでもPythonみたいに関数内に関数を定義できねーかなぁと考えている弊社です。

あと、出来ればVBでもカリー化関数を扱いたい。 もっとカジュアルに関数を扱いたいのでMicrosoftさんオナシャス。

とまぁ、儚い夢を願ったところで本題です。 VBには3項演算子*1と3項演算子っぽい何かがあるのは生産性アプリケーションを書いている皆さんには常識的な事だと思います。

旧来からある(らしい)IIf関数と割と新顔(らしい)*2If演算子の二つです。 まぁ、関数ってある時点で色々と察しがつくのですが、とりあえずmsdnを見てみましょう。

If 演算子 (Visual Basic) | Microsoft Docs

IIf 関数 | Microsoft Docs

IfIIfの違いは上のリンク内に書いてあるので使い方の説明は割愛します。

Let's IL

Dim result = IIf(True, a(), b())

IIfの実態はMicrosoft.VisualBasic.Interactionに属する3項演算子っぽい働きをする関数です。 よって関数の呼び出し前に3つの式が評価されてからIIfが呼ばれます。

  .maxstack  3
  .locals init ([0] object result)
  IL_0000:  nop
  IL_0001:  ldc.i4.1
  IL_0002:  call       int32 SectionOperator.Module1::a()
  IL_0007:  box        [mscorlib]System.Int32
  IL_000c:  call       int32 SectionOperator.Module1::b()
  IL_0011:  box        [mscorlib]System.Int32
  IL_0016:  call       object [Microsoft.VisualBasic]Microsoft.VisualBasic.Interaction::IIf(bool,
                                                                                            object,
                                                                                            object)
  IL_001b:  call       object [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::GetObjectValue(object)
  IL_0020:  stloc.0
  IL_0021:  nop
  IL_0022:  ret

resultにはa()の返り値が格納されますが、IIf呼び出し前にa()b()の両方が呼び出さています。 なのでa()b()などに副作用がある場合は予想に反する動きをして戸惑うかもしれません。

また、IIfは返り値がIIf(bool, object, object) -> objectなのでOption Strict Onとはキャストのオンパレードとなり相性が悪いと思います。

Dim result = If(True, a(), b())
  .maxstack  1
  .locals init ([0] int32 result)
  IL_0000:  nop
  IL_0001:  ldc.i4.1
  IL_0002:  brtrue.s   IL_000b
  IL_0004:  call       int32 SectionOperator.Module1::b()
  IL_0009:  br.s       IL_0010
  IL_000b:  call       int32 SectionOperator.Module1::a()
  IL_0010:  stloc.0
  IL_0011:  nop
  IL_0012:  ret

If演算子では条件によってどちらか一方しか評価されません。 また、IIfのように明示的にキャストする必要もないのでこちらの方が便利ですね。

まとめ

以上のことから今のご時世IIfを使うメリットはほぼ無いと思います。 むしろIIfを使い必ず副作用を発生させないといけないような設計をしている場合は今すぐ改めるべきです。

おわり

*1:正確には条件演算子って言うらしいんですけどココでは3項演算子の言葉を使います

*2:VS 2008からっぽい