Visual Basic 初級講座 [改訂版]
VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

第26回 Nothing

2020/11/29

この記事が対象とする製品・バージョン

VB2019 Visual Basic 2019 対象です。
VB2017 Visual Basic 2017 対象です。
VB2015 Visual Basic 2015 対象です。
VB2013 Visual Basic 2013 対象です。
VB2012 Visual Basic 2012 対象です。
VB2010 Visual Basic 2010 対象です。
VB2008 Visual Basic 2008 対象です。
VB2005 Visual Basic 2005 対象です。
VB.NET 2003 Visual Basic.NET 2003 対象外ですがほとんどの説明があてはまるので参考になります。
VB.NET 2002 Visual Basic.NET (2002) 対象外ですがほとんどの説明があてはまるので参考になります。
VB6対応 Visual Basic 6.0 × 対象外です。

 

目次

1.Nothing の発生

前回説明したように Nothing (読み方:Nothing=ナッシング)とは何も参照していない(=実体がない)ことを示す特別な値です。C#など他の言語では null (ヌル) とも呼びます。

Nothing はどういう場合に出現するでしょうか?少し復習しておきましょう。

構造体については Nothing という状態はありえないので、Nothing が出現するのはクラスを扱っている場合です。

 

変数の値が Nothing になるのは次のどちらかの場合です。

・参照型(=クラス)の変数が初期化されていない。この場合、初期値として Nothing が割り当てられます。

・変数に Nothing を代入している。

 

変数を初期化していないで Nothing になる例を紹介します。

VB2002 VB2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Dim s1 As String         'Nothing になります。(String はクラス)
Dim r1 As Random         'Nothing になります。(Random はクラス)
Dim f1 As IO.FileInfo    'Nothing になります。(FileInfo はクラス)

 

次の例も初期化していませんが、構造体なので Nothing にはなりません。

VB2002 VB2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Dim i1 As Integer        '初期値 0 が割り当てられます。
Dim d1 As Date           '初期値 西暦1年1月1日0時0分が割り当てられます。
Dim p1 As Point          '座標(0, 0)が割り当てられます。(Point は構造体なのでNothingにならない。)

 

変数に Nothing を代入するには次のようにします。

VB2002 VB2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019


r1 = Nothing

 

 

2.null許容型

構造体については、言語機能上は「構造体にはNothingはありえません!」というのがすべてです。

しかし、プログラムしているときは、構造体に Nothing が欲しくなる場合があります。たとえば、年齢を入力させるプログラムを想像してみます。プログラムないでは age という Integer型の整数で年齢を保持することにします。ここまでは何の問題ありません。

ところが、「年齢を入力したくない人もいるから年齢は省略可能にしよう」と思ったらどうしたらよいでしょうか? このように意味的には構造体でも実体が存在しないという状況がありえます。

簡易的にプログラムすると次のようなイメージです。構造体には値がないことを示す Nothing を代入できないため、「値がない」という情報を保持できないというわけです。

VB2002 VB2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

'これはまったく問題ない
Dim age1 As Integer
age1 = 24
Debug.WriteLine(age1)

'構造体はNothingにできないので、age2 の値は 0 になってしまう。
Dim age2 As Integer
age2 = Nothing
Debug.WriteLine(age2)

Debug.WriteLineで出力される場所

 

この問題を解決するためにVB2005のときに「null許容型」(ヌルきょようがた)という仕掛けが導入されました。

Nullable構造体(読み方:Nullable=ヌレイブル) というものを使って、実体があるのかないのかという情報を保持するという仕掛けです。すぐあとで説明する記号 ? を使った便利な書き方があるので、Nullableを直接プログラム上に記述することはほぼないのですが、何が起こっているか理解するために紹介しておきます。

IntegerのNullable, LongのNullableなどいろいろなNullableが考えられるのでNullableはジェネリックになっており、Nullable(Of Integer)のように使用します。

VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

'これはまったく問題ない
Dim age1 As Nullable(Of Integer)
age1 = 24
Debug.WriteLine(age1)

'こちらはNothingなので何も出力されない。
Dim age2 As Nullable(Of Integer)
age2 = Nothing
Debug.WriteLine(age2)

このように Integer を Nullable(Of Integer) に書き換えただけで、Nothing を保持してくれるようになります。

 

VB2008になると、null許容型はVBの言語機能と統合され、Nullable(Of Xxxx)のことを、Xxxx? と書く特別な書き方ができるようになりました。こちらの方が簡単なので、この機能が必要な場合は、ほとんどの人がNullableとは書かずに ? と書きます。

VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

'これはまったく問題ない
Dim age1 As Integer?
age1 = 24
Debug.WriteLine(age1)

'こちらはNothingなので何も出力されない。
Dim age2 As Integer?
age2 = Nothing
Debug.WriteLine(age2)

なお、変数名の方に ? を付けて Dim age1? As Integer と書いても意味は同じです。

 

null許容型は便利ですが、本当に値が Nothing になっているわけではありません。HasValue(読み方:HasValue=ハズバリュー)というフラグを使って実体あり/なし を単なるBoolean型の情報として記録しているだけです。VBは、うまくこの仕組みを使いやすくしているので、このことを意識しなくても良いことも多いのですが、やはり、Nothing とは異なるものなので、私はこれを「Nothing」と呼ぶことに抵抗があります。そこで、この初級講座ではこの状態を「Nothing相当」と表現する場合があります。この表現は、独特なもので他では通用しないと思います。他で通用するように表現するには「HasValueがFalseである Nullable構造体」など周りくどい表現になります。

例:「age2 は HasValue が False であるNullable(Of Integer)構造体です。」 = 「age2 は Nothing相当です。」(この初級講座独特の表現) = 「age2 は Nothing です。」(私は抵抗がありますが、文脈によっては使用される表現)

 

さて、上記の例はInteger? と書いてあっても実体は Nullable(Of Integer)です。そのため Nullable構造体のメンバーを呼び出せます。

たとえば、実体のある値が存在するかを示すHasValueプロパティを直接見ることもできます。

VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

If age2.HasValue Then
    Debug.WriteLine("age2には値があります。")
Else
    Debug.WriteLine("age2には値がありません。つまり、Nothing相当です。")
End If

Valueプロパティから直接値を取り出すこともできます。

 

メモ メモ  - 昔のプログラマー

年齢を省略可能にしたい?それなら、省略されたときは -1 を代入しておくようにしよう。-1 はありえないからちょうどいい目印になるね。

え?マイナスの値は入れたくない? どうして? まぁどうしても入れたくないなら 省略されたときは 999 にしておけばいいんじゃないかな?999もありえないでしょう。

え?生年月日が 1021年 になるバグが発生? いや、そのまま引き算しちゃダメでしょう。そこは If 文入れておいてよ。心配なら年齢が省略されたことを示す Boolean型の変数を用意しておくか、たとえば、 IsAgeEmpty みたいな名前で。これで妙なバグもなくなるでしょう。あー、でも省略時 0 になっちゃうのはちょっと嫌かもね。まぁ気をつけてプログラムしてもらえば大丈夫だよ。そんなに大変じゃないでしょう。

え?もう文字列型にしちゃう??

 

 

3.Nothing の判定

3-1.注意が必要な Nothing

Nothingになるかどうか、少し注意したほうが良いのは、何かの変数やプロパティやメソッドなどの戻り値を代入する場合です。

次の 変数 x1, x2 ,x3 が Nothing になるかどうかはプログラムを見ただけではわかりません。

VB2002 VB2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

x1 = z1
x2 = Me.Prop1
x3 = Me.GetName()

z1 が Nothing なら x1 は Nothingになります。Me の Prop1 が Nothing なら x2 は Nothing になります。GetName() メソッドが Nothing を返すなら x3 も Nothing になります。

 

変数が Nothing の状態でその変数からメソッドやプロパティを呼び出そうとすると NullReferenceExceptionの例外が発生します。

NullReferenceExceptionは Nothing の変数を使ってメンバー(=メソッドやプロパティなど)を呼び出そうとして時に発生するのであって、Nothingの値を参照するだけであれば発生しません。

 

次の例は ShowDialogメソッドを呼び出そうとしているので、変数dialog が Nothing であれば NullReferenceException になります。

VB2002 VB2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019


dialog.ShowDialog

一方、次の例は、Xxxx メソッドの引数に dialog を指定しているだけで、dialogを使ってメンバーを呼び出そうとしていません。そのため、これだけであればNullReferenceExceptionは発生しません。

VB2002 VB2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019


Xxxx(dialog)

Xxxxメソッドは内部で引数として渡されたdialogのメンバーを呼び出そうとするとそのときにNullReferenceExceptionが発生する可能性はあります。また、メソッド側で引数に Nothing が渡されることを想定していないこともよくあり、ArgumentNullExceptionを生成するものもあります。

 

3-2.Nothingの判定

さて、NullReferenceExceptionを防ぐために、変数の値が Nothing である可能性があるのならば、そのメンバーを呼び出す前に、値がNothingであるかどうか確認して処理を分岐させる必要があります。

それには、変数 Is Nothing を使用します。 = Nothing ではないのがポイントです。

VB2002 VB2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Dim dialog As ColorDialog

If dialog Is Nothing Then
    MsgBox("dialogがNothingです!")
Else
    dialog.ShowDialog
End If

変数 IsNot Nothing を使うと、変数がNothingでないことの確認ができます。

VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Dim dialog As New ColorDialog

If dialog IsNot Nothing Then
    dialog.ShowDialog
End If

 

この Is Nothng 、IsNot Nothingは Integer? などを使って Nothing が設定されているNullable構造体に対しても Nothing が設定されているかどうか判断してくれます。

VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Dim age As Integer?

If age Is Nothing Then
    Debug.WriteLine("ageはNothing相当です。")
End If

 

メモ メモ  - IsNothing

IsNothingという関数もあり、意味はIs Nothingと同じです。

しかし、IsNothingの方がスピードが少し遅いので使う意味はありません。

VB2002 VB2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Dim dialog As New ColorDialog

'この方法はお勧めしません。
If Not IsNothing(dialog) Then
    dialog.ShowDialog
End If

IsNothingは、構造体に対しても判定を実行してくれます。構造体は Nothing になることはないので常に False を返します。Is Nothingはそもそも構造体を相手にすることはできず、構造体を指定するとビルドエラーになります。(ただ、Integer? の形式で表現されるNullable構造体は例外として、正常に判断してくれます。)

構造体ではNullReferenceExceptionがありえないので、Nothingであるかどうか、気にする必要はほぼなく、この余計な機能のせいで IsNothing は少し遅いのだと思います。

マイクロソフトさん、もう IsNothing は廃止していただいて全然OKですよ。

 

4.Null条件演算子

Nothingの可能性があるものに逐一 If xxx Is Nothing Then を付けるのは面倒なので、VB2015からは 記号 ?. (はてなドット) を使った簡略な書き方が登場しました。

これを「Null 条件演算子」(ヌル じょうけんえんざんし)と呼びます。

 

メンバーを呼び出すときに . (ドット) の代わりに、 ?. (はてなドット) を使うと Nothing でなければメンバーを呼び出す という意味になります。

VB2015 VB2017 VB2019

obj?.Member()

 

戻り値を受け取ることもできます。obj が Nothing の場合、Member は呼び出されず、x は Nothing または、Nothingを表すNullableの値になります。どちらになるかは、Memberが参照型(クラス)であるか、値型(構造体)であるかで変わります。

VB2015 VB2017 VB2019

x = obj?.Member

 

配列やコレクションなどで要素を参照する ( ) も ?( ) と記述することができます。obj が Nothing の場合、上記同様、x は Nothing または、Nothingを表すNullableの値になります。objがNothingでない場合は obj(0) を取得して x に代入します。

VB2015 VB2017 VB2019

x = obj?(0)

なお、 ?( ) は Nothing 以外の面倒は見てくれません。たとば、配列の要素の数が 0 の場合 obj(0) は IndexOutofRangeException になりますが、 これは Nothing とは関係ないので ?( ) を使っても防げません。

 

 

例1:次のプログラムは dialog が Nothing でなければ、ShowDialog メソッドを呼び出します。Nothingであれば、ShowDialogメソッドは呼び出されません。

VB2015 VB2017 VB2019


dialog?.ShowDialog

 

例2:次の例では obj1 が Nothing の場合、 value1 は Nothingになります。

VB2015 VB2017 VB2019


Dim value1 As String = obj1?.Prop1

 

例3:次の例では、value2 は Nothingを意味するNullable(Of Integer)になります。Integerが値型なので上記の例と違いが出ます。

VB2015 VB2017 VB2019


Dim value2 As Integer? = obj1?.Prop2

 

Null条件演算子のすばらしいところは多段階での適用です。次のような書き方ができます。

VB2015 VB2017 VB2019


Dim value As Integer = obj1?.obj2?.Prop1

この例では obj1 が Nothing であるか、 obj1.obj2 が Nothing である場合は、代入は実行されず value の値は変更されません。(つまり、初期値の 0 になります。)

 

?. の登場以前は、Is Nothing で記述してたので、同じプログラムが次のようになってしまっていました。

VB2002 VB2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Dim value As Integer

If obj1 IsNot Nothing Then
    If obj1.obj2 IsNot Nothing Then
        value = obj1.obj2.Prop1
    End If
End If

 

5.まとめ

値が Nothing であるか確認する方法

If value Is Nothing Then

 

値が Nothing でないことを確認する方法

If value IsNot Nothing Then

 

構造体に Nothing相当の情報を持てるようにする方法

Dim x As Integer?

 

Nothingでなければメンバーを呼び出す方法

obj?.Member

x = obj?.Member

x = obj?(0)

 

 

メモ メモ  x ??= v1 ?? v2 ?? v3?.ToString()

ところで、C# にはこの他に Nothing(C#だと null) でなければ別の値を指定する ?? 演算子や、Nothing でなければ代入操作を実行する ??= 演算子があります。

これらがあると、プログラムの量は少なくなるのですが、プログラム内に ? がたくさん出現することに・・・。

Nothing(null)を簡単に扱えるようにすることは時代の要請です。2020年現在、アプリケーションは他のアプリケーションと通信することが普通です。巨大な多機能のアプリケーションを作るのではなく、小さな単機能のアプリケーションを作り、アプリケーション同士が連携して結果としていろいろな機能を実現するという方式が好まれます。

各アプリケーションのバージョンアップの頻度も高いので、『必ずこの情報が来る』という前提でプログラムすることはむずかしくなってきています。この情報がくれば、この処理をする、この情報がこの来なければこの処理は飛ばす・・・のようなことがいろいろなところで発生します。いちいち If で判断したくないんです。あったらやる、なかったらやらない。obj?.DoIt です。