Nothing と null 状態

Nothing は C# でいう null であるという認識だと思いますが、Nothing キーワードとしての Nothing と、とある変数が、インスタンスが生成されているのかいないのか、つまり現在のインスタンスの状態としての Nothing 状態についておさらいしたいと思います。ポイントは構造体(値型)の場合と、クラス(参照型)の場合で振る舞いが違うことです。

目次

Class と Nothing

これが一番分かりやすい動きですが、クラス型の変数に Nothing が入っている場合、インスタンス生成されておらず、操作しようとすると NullReferenceException が発生してしまいます。

Class Class1
    Public Number As Integer = 0
End Class

Module Module1

    Sub Main()

        Dim obj As Class1 = Nothing
        Dim result = obj.Number


        Console.ReadKey()
    End Sub

End Module

出力結果

System.NullReferenceException: 'オブジェクト参照がオブジェクト インスタンスに設定されていません。'

obj が Nothing でした。

必ずメンバーアクセスする前に、インスタンス生成するか、Nothing チェックしないといけないね、と分かります。

Structure と Nothing

続いて構造体の場合です。インスタンス生成していませんが、構造体の場合、宣言と同時に暗黙的にインスタンス生成されます。よって以下のように明示的にインスタンス生成しないで使うことができます(個人的には気持ち悪いので、明示的にインスタンス生成したいです)。

Structure Structure1
    Public Number As Integer
End Structure

Module Module1

    Sub Main()

        Dim obj As Structure1
        Console.WriteLine(obj.Number)

        Console.ReadKey()
    End Sub

End Module

出力結果

0

また、Structure に対して Nothing をセットすると、各 Structure 毎の規定値がセットされます。私たちが自前で作るような Structure の規定値はインスタンス生成です。同じ Structure でも、プリミティブ型の Structure である、Integer, Boolean, Date などは、それぞれ、0, False, Date.MinValue となります。

自前作成の構造体の場合、以下のように書いてもインスタンス生成されたものとして動作します。ただし混乱の元でしかないので、Dim obj As New Structure1 と書いた方が健康でいられる気がします。

Structure Structure1
    Public Number As Integer
End Structure

Module Module1

    Sub Main()

        Dim obj As Structure1 = Nothing
        Console.WriteLine(obj.Number)

        Console.ReadKey()
    End Sub

End Module

出力結果

0

こちらはプリミティブ型の場合。

Module Module1

    Sub Main()

        Dim i1 As Integer = Nothing
        Dim s1 As String = Nothing
        Dim b1 As Boolean = Nothing
        Dim d1 As Date = Nothing

        If i1 = Nothing Then Console.WriteLine("i1 は、Nothing でした")
        If s1 = Nothing Then Console.WriteLine("s1 は、Nothing でした1")
        If s1 Is Nothing Then Console.WriteLine("s1 は、Nothing でした2")
        If b1 = Nothing Then Console.WriteLine("b1 は、Nothing でした")
        If d1 = Nothing Then Console.WriteLine("d1 は、Nothing でした")
        Console.WriteLine("")

        i1 = 0
        s1 = ""
        b1 = False
        d1 = Date.MinValue

        If i1 = Nothing Then Console.WriteLine("i1 は、Nothing でした")
        If s1 = Nothing Then Console.WriteLine("s1 は、Nothing でした1")
        If s1 Is Nothing Then Console.WriteLine("s1 は、Nothing でした2")
        If b1 = Nothing Then Console.WriteLine("b1 は、Nothing でした")
        If d1 = Nothing Then Console.WriteLine("d1 は、Nothing でした")


        Console.ReadKey()
    End Sub

End Module

出力結果

i1 は、Nothing でした
s1 は、Nothing でした1
s1 は、Nothing でした2
b1 は、Nothing でした
d1 は、Nothing でした

i1 は、Nothing でした
s1 は、Nothing でした1
b1 は、Nothing でした
d1 は、Nothing でした

Structure である Integer, Boolean, Date はそれぞれ規定値が、0, False, Date.MinValue ですので Nothing をセットすると各規定値がセットされます。つまり Structure では Nothing と規定値は同等であり、null 状態という意味ではありません。

一方で Class である String ですが、クラスなので Nothing をセットすると null 状態という意味の方になります。なのですが、プリミティブ型として振舞いたいため、規定値である空文字と Nothing を同等とみなす動きをします。

Module Module1

    Sub Main()

        If "" = Nothing Then
            Console.WriteLine("True")
        End If

        If "" Is Nothing Then
            Console.WriteLine("True")
        Else
            Console.WriteLine("False")
        End If


        Console.ReadKey()
    End Sub

End Module

出力結果

True
False

空文字と Nothing が同じであるかどうかを = で比較している場合はプリミティブ型として考えるので、規定値は空文字なので True と扱われます。しかし、Is で比較する場合は、クラスとしてインスタンス生成してあるかどうかを見るので False という結果になります。String クラスとしては、空文字はインスタンス生成後の値ということですね。

まとまると

  • クラスに対する Nothing は、インスタンス生成しているのか、していないのか(null 状態なのか)
  • 構造体に対する Nothing は、null 状態なのかどうか ではなく 各 Structure に合った規定値かどうか

くらいに場合分けで覚えておいた方が良いのではないかと思います。

頭が痛くなってきた

申し訳ないです。皆さんを困らせるために書いたのではないのです。知れば知るほど知りたくなかったというのも、まぁ通る道なのかなと思ったり。