If演算子

タグの編集
投稿者 まこ  (社会人) 投稿日時 2021/2/26 00:34:02
If演算子でnull許容値型の結果を得たい場合、以下では変数retがFaseとなります。
自分の中では、If演算子において第1引数がTrueなら第2引数を、
第1引数がFalseなら第3引数を返す認識でしたのでNothingが返る事を期待しました。

null許容値型の場合は何故このようになるのでしょうか?
又、If演算子でNothingを返すような書き方はどうすればいいですか?

 
    Private Sub Button1_Click(sender As Object, e As RoutedEventArgs) Handles Button1.Click
        Dim flag As Boolean = False
        Dim ret As Boolean? = If(flag, TrueNothing)
    End Sub
 
投稿者 るきお  (社会人) 投稿日時 2021/2/26 08:48:13
If演算子の第2引数と第3引数は同じ型である必要があります。
たとえば、次のプログラムはコンパイルエラーとなり実行できません。

Dim ret As Boolean? = If(flag, True"ABC")


まこさん、提示されたコードの場合、第2引数が True 、第3引数が Nothing です。
Nothing なので型が不明瞭で、気になってどういう動作になるのか調べてみました。

この場合、第2引数が True なので、VBはこれを Boolean 型であると解釈していました。(ILSpyで確認しました。)
そして(わかりにくい点なのですが)、Nothingは自動的にBooleanである、False に変換されていました。

そのため、このプログラムは、
Dim ret As Boolean? = If(flag, TrueNothing)


VBにはこのように解釈されています。
Dim ret As Boolean? = If(flag, TrueFalse)


この動作はOption Strict Onでも発生します。

やりたいことを実現するには普通の If を使うことになりそうです。
If flag Then
    ret = True
End If



ちなみに C# では従来下記プログラムはコンパイルエラーで実行できませんでした。
bool? ret = flag ? true : null;


しかし、最新のC# 9.0 からは実行でき、flag が falseの場合、うまく ret は null になります。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2021/2/26 09:13:07
VB の Nothing は文脈に応じて、C# でいうところの null と default のいずれかになります。

Dim a As Integer = Nothing  'CInt(Nothing) すなわち 0 
Dim b As Boolean = Nothing  'CBool(Nothing) すなわち False 


そして、「If(flag, True, Nothing)」という構文については、
「True もしくは False となる Boolean 型」と解釈されます。
この時、代入式の左辺の型は関係ありません。

ちなみに型推論を伴わない IIf を使った場合、
IIf(flag, True, Nothing)
という式は、「True もしくは Nothing となる Object 型」と解釈されます。


> If演算子でNothingを返すような書き方はどうすればいいですか?
Null 許容値型となるリテラル構文は無いので、型変換が必要です。

If(flag, True, CType(Nothing, Boolean?))
If(flag, CType(True, Boolean?), Nothing)
If(flag, CType(True, Boolean?), CType(Nothing, Boolean?))

なお、Option Strict Off の場合においては、
 Dim ret As Boolean? = If(flag, True, CObj(Nothing))
によって、Nothing 代入を果たせます。
投稿者 まこ  (社会人) 投稿日時 2021/2/26 09:34:42
るきお様、魔界の仮面弁士様、ありがとうございます。

>If演算子の第2引数と第3引数は同じ型である必要があります。
るきお様のこの発言でおもいっきり、腑に落ちました。
今後、If演算子を使う場合は常に、この事を意識しようと思います。

魔界の仮面弁士様が提示くださった、以下もそういう事だと認識しました。
この手のコーディングはIf演算子を使ってワンライナーで書く癖がついているので
null許容値型の場合だけ「IF-ELSE-END IF構文」を使うのは抵抗があったので助かりました。

If(flag, True, CType(Nothing, Boolean?))
If(flag, CType(True, Boolean?), Nothing)
If(flag, CType(True, Boolean?), CType(Nothing, Boolean?))

どうも、ありがとうございました。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2021/2/26 11:48:17
>> If演算子の第2引数と第3引数は同じ型である必要があります。

If 演算子のこの動作は、「共通型の推論」と呼ばれています。

「同じ型」ならばもちろん問題無いわけですが、より正確に言えば、
  『一方が他方に対する拡大変換を持つ必要がある』
ということになります。

If(f, CByte(1), CInt(2)) などは OK ですが、
If(f, CByte(1), CSByte(2)) の場合は NG ですね。


🆗拡大解釈なので、If 演算子が動作します。
Option Strict On
Module Module1
    Sub Main()
        Dim スライム As New Slime()
        Dim メタルスライム As New MetalSlime()
        Dim f As Boolean = False

        'monser As Slime としてコンパイルされる 
        Dim monster = If(f, スライム, メタルスライム)
    End Sub
End Module

Class Slime
End Class
Class MetalSlime
    Inherits Slime
    Implements IMetalic
End Class
Interface IMetalic
End Interface



🆖縮小変換になるので、共通型を推論できません。
Option Strict On
Module Module1
    Sub Main()
        Dim スライム As New Slime()
        Dim メタルスライム As New MetalSlime()
        Dim f As Boolean = False

        'エラー BC36912: 共通型を推論できません。Option Strict On が設定されているため、 
        '                'Object' と見なすことはできません。 
        Dim monster As ISlime = If(f, スライム, メタルスライム)
    End Sub
End Module

NotInheritable Class Slime
    Implements ISlime
End Class
NotInheritable Class MetalSlime
    Implements ISlime, IMetalic
End Class
Interface ISlime
End Interface
Interface IMetalic
End Interface
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2021/2/26 12:42:35
> Null 許容値型となるリテラル構文は無いので、型変換が必要です。

そういえば、Nullable(Of Boolean) のコンストラクタを呼び出すという手がありました。

' いずれも As Boolean? として解釈される 
Dim ret1 = If(f, TrueNew Boolean?())
Dim ret2 = If(f, New Boolean?(True), Nothing)
投稿者 まこ  (社会人) 投稿日時 2021/2/26 15:17:44
魔界の仮面弁士様、追記、ありがとうございます。
勉強になります。

私としては、第2、3引数をキャストして同じ型で扱う方法が一番しっくりくるので
多少、冗長的でも今後は教えてもらった、以下のようにコーディングしようと思っています。
If(flag, CType(True, Boolean?), CType(Nothing, Boolean?))

こう書けば、コンパイラが「IDE0004」の警告を出してくれて、クイックアクションで修正もできるし、
拡大変換、縮小変換を考えなくていいので、シンプルな考え方でいけるので。。。。

>『一方が他方に対する拡大変換を持つ必要がある』
この事は頭の片隅には、置いておきます。

どうも、ありがとうございました。