Visual Basic 初級講座 [改訂版] |
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() |
Visual Basic 中学校 > 初級講座[改訂版] >
2020/11/29
この記事が対象とする製品・バージョン
![]() |
Visual Basic 2019 | ◎ | 対象です。 |
![]() |
Visual Basic 2017 | ◎ | 対象です。 |
![]() |
Visual Basic 2015 | ◎ | 対象です。 |
![]() |
Visual Basic 2013 | ◎ | 対象です。 |
![]() |
Visual Basic 2012 | ◎ | 対象です。 |
![]() |
Visual Basic 2010 | ◎ | 対象です。 |
![]() |
Visual Basic 2008 | ◎ | 対象です。 |
![]() |
Visual Basic 2005 | ◎ | 対象です。 |
![]() |
Visual Basic.NET 2003 | △ | 対象外ですがほとんどの説明があてはまるので参考になります。 |
![]() |
Visual Basic.NET (2002) | △ | 対象外ですがほとんどの説明があてはまるので参考になります。 |
![]() |
Visual Basic 6.0 | × | 対象外です。 |
前回説明したように Nothing (読み方:Nothing=ナッシング)とは何も参照していない(=実体がない)ことを示す特別な値です。C#など他の言語では null (ヌル) とも呼びます。
Nothing はどういう場合に出現するでしょうか?少し復習しておきましょう。
構造体については Nothing という状態はありえないので、Nothing が出現するのはクラスを扱っている場合です。
変数の値が Nothing になるのは次のどちらかの場合です。
・参照型(=クラス)の変数が初期化されていない。この場合、初期値として Nothing が割り当てられます。
・変数に Nothing を代入している。
変数を初期化していないで Nothing になる例を紹介します。
Dim s1 As
String
'Nothing になります。(String はクラス) Dim r1 As Random 'Nothing になります。(Random はクラス) Dim f1 As IO.FileInfo 'Nothing になります。(FileInfo はクラス) |
次の例も初期化していませんが、構造体なので Nothing にはなりません。
Dim i1 As
Integer
'初期値 0 が割り当てられます。 Dim d1 As Date '初期値 西暦1年1月1日0時0分が割り当てられます。 Dim p1 As Point '座標(0, 0)が割り当てられます。(Point は構造体なのでNothingにならない。) |
変数に Nothing を代入するには次のようにします。
r1 = Nothing |
構造体については、言語機能上は「構造体にはNothingはありえません!」というのがすべてです。
しかし、プログラムしているときは、構造体に Nothing が欲しくなる場合があります。たとえば、年齢を入力させるプログラムを想像してみます。プログラムないでは age という Integer型の整数で年齢を保持することにします。ここまでは何の問題ありません。
ところが、「年齢を入力したくない人もいるから年齢は省略可能にしよう」と思ったらどうしたらよいでしょうか? このように意味的には構造体でも実体が存在しないという状況がありえます。
簡易的にプログラムすると次のようなイメージです。構造体には値がないことを示す Nothing を代入できないため、「値がない」という情報を保持できないというわけです。
'これはまったく問題ない Dim age1 As Integer age1 = 24 Debug.WriteLine(age1) '構造体はNothingにできないので、age2 の値は 0 になってしまう。 Dim age2 As Integer age2 = Nothing Debug.WriteLine(age2) |
この問題を解決するためにVB2005のときに「null許容型」(ヌルきょようがた)という仕掛けが導入されました。
Nullable構造体(読み方:Nullable=ヌレイブル) というものを使って、実体があるのかないのかという情報を保持するという仕掛けです。すぐあとで説明する記号 ? を使った便利な書き方があるので、Nullableを直接プログラム上に記述することはほぼないのですが、何が起こっているか理解するために紹介しておきます。
IntegerのNullable, LongのNullableなどいろいろなNullableが考えられるのでNullableはジェネリックになっており、Nullable(Of Integer)のように使用します。
'これはまったく問題ない 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とは書かずに ? と書きます。
'これはまったく問題ない 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プロパティを直接見ることもできます。
If age2.HasValue
Then Debug.WriteLine("age2には値があります。") Else Debug.WriteLine("age2には値がありません。つまり、Nothing相当です。") End If |
Valueプロパティから直接値を取り出すこともできます。
年齢を省略可能にしたい?それなら、省略されたときは -1 を代入しておくようにしよう。-1 はありえないからちょうどいい目印になるね。 え?マイナスの値は入れたくない? どうして? まぁどうしても入れたくないなら 省略されたときは 999 にしておけばいいんじゃないかな?999もありえないでしょう。 え?生年月日が 1021年 になるバグが発生? いや、そのまま引き算しちゃダメでしょう。そこは If 文入れておいてよ。心配なら年齢が省略されたことを示す Boolean型の変数を用意しておくか、たとえば、 IsAgeEmpty みたいな名前で。これで妙なバグもなくなるでしょう。あー、でも省略時 0 になっちゃうのはちょっと嫌かもね。まぁ気をつけてプログラムしてもらえば大丈夫だよ。そんなに大変じゃないでしょう。 え?もう文字列型にしちゃう?? |
Nothingになるかどうか、少し注意したほうが良いのは、何かの変数やプロパティやメソッドなどの戻り値を代入する場合です。
次の 変数 x1, x2 ,x3 が Nothing になるかどうかはプログラムを見ただけではわかりません。
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 になります。
dialog.ShowDialog |
一方、次の例は、Xxxx メソッドの引数に dialog を指定しているだけで、dialogを使ってメンバーを呼び出そうとしていません。そのため、これだけであればNullReferenceExceptionは発生しません。
Xxxx(dialog) |
Xxxxメソッドは内部で引数として渡されたdialogのメンバーを呼び出そうとするとそのときにNullReferenceExceptionが発生する可能性はあります。また、メソッド側で引数に Nothing が渡されることを想定していないこともよくあり、ArgumentNullExceptionを生成するものもあります。
さて、NullReferenceExceptionを防ぐために、変数の値が Nothing である可能性があるのならば、そのメンバーを呼び出す前に、値がNothingであるかどうか確認して処理を分岐させる必要があります。
それには、変数 Is Nothing を使用します。 = Nothing ではないのがポイントです。
Dim dialog
As ColorDialog If dialog Is Nothing Then MsgBox("dialogがNothingです!") Else dialog.ShowDialog End If |
変数 IsNot Nothing を使うと、変数がNothingでないことの確認ができます。
Dim dialog
As New ColorDialog If dialog IsNot Nothing Then dialog.ShowDialog End If |
この Is Nothng 、IsNot Nothingは Integer? などを使って Nothing が設定されているNullable構造体に対しても Nothing が設定されているかどうか判断してくれます。
Dim age
As Integer? If age Is Nothing Then Debug.WriteLine("ageはNothing相当です。") End If |
IsNothingという関数もあり、意味はIs Nothingと同じです。 しかし、IsNothingの方がスピードが少し遅いので使う意味はありません。
IsNothingは、構造体に対しても判定を実行してくれます。構造体は Nothing になることはないので常に False を返します。Is Nothingはそもそも構造体を相手にすることはできず、構造体を指定するとビルドエラーになります。(ただ、Integer? の形式で表現されるNullable構造体は例外として、正常に判断してくれます。) 構造体ではNullReferenceExceptionがありえないので、Nothingであるかどうか、気にする必要はほぼなく、この余計な機能のせいで IsNothing は少し遅いのだと思います。 マイクロソフトさん、もう IsNothing は廃止していただいて全然OKですよ。 |
Nothingの可能性があるものに逐一 If xxx Is Nothing Then を付けるのは面倒なので、VB2015からは 記号 ?. (はてなドット) を使った簡略な書き方が登場しました。
これを「Null 条件演算子」(ヌル じょうけんえんざんし)と呼びます。
メンバーを呼び出すときに . (ドット) の代わりに、 ?. (はてなドット) を使うと Nothing でなければメンバーを呼び出す という意味になります。
obj?.Member()
戻り値を受け取ることもできます。obj が Nothing の場合、Member は呼び出されず、x は Nothing または、Nothingを表すNullableの値になります。どちらになるかは、Memberが参照型(クラス)であるか、値型(構造体)であるかで変わります。
x = obj?.Member
配列やコレクションなどで要素を参照する ( ) も ?( ) と記述することができます。obj が Nothing の場合、上記同様、x は Nothing または、Nothingを表すNullableの値になります。objがNothingでない場合は obj(0) を取得して x に代入します。
x = obj?(0)
なお、 ?( ) は Nothing 以外の面倒は見てくれません。たとば、配列の要素の数が 0 の場合 obj(0) は IndexOutofRangeException になりますが、 これは Nothing とは関係ないので ?( ) を使っても防げません。
例1:次のプログラムは dialog が Nothing でなければ、ShowDialog メソッドを呼び出します。Nothingであれば、ShowDialogメソッドは呼び出されません。
dialog?.ShowDialog |
例2:次の例では obj1 が Nothing の場合、 value1 は Nothingになります。
Dim value1 As String = obj1?.Prop1 |
例3:次の例では、value2 は Nothingを意味するNullable(Of Integer)になります。Integerが値型なので上記の例と違いが出ます。
Dim value2 As Integer? = obj1?.Prop2 |
Null条件演算子のすばらしいところは多段階での適用です。次のような書き方ができます。
Dim value As Integer = obj1?.obj2?.Prop1 |
この例では obj1 が Nothing であるか、 obj1.obj2 が Nothing である場合は、代入は実行されず value の値は変更されません。(つまり、初期値の 0 になります。)
?. の登場以前は、Is Nothing で記述してたので、同じプログラムが次のようになってしまっていました。
Dim value As Integer If obj1 IsNot Nothing Then If obj1.obj2 IsNot Nothing Then value = obj1.obj2.Prop1 End If End If |
値が 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)
ところで、C# にはこの他に Nothing(C#だと null) でなければ別の値を指定する ?? 演算子や、Nothing でなければ代入操作を実行する ??= 演算子があります。 これらがあると、プログラムの量は少なくなるのですが、プログラム内に ? がたくさん出現することに・・・。 Nothing(null)を簡単に扱えるようにすることは時代の要請です。2020年現在、アプリケーションは他のアプリケーションと通信することが普通です。巨大な多機能のアプリケーションを作るのではなく、小さな単機能のアプリケーションを作り、アプリケーション同士が連携して結果としていろいろな機能を実現するという方式が好まれます。 各アプリケーションのバージョンアップの頻度も高いので、『必ずこの情報が来る』という前提でプログラムすることはむずかしくなってきています。この情報がくれば、この処理をする、この情報がこの来なければこの処理は飛ばす・・・のようなことがいろいろなところで発生します。いちいち If で判断したくないんです。あったらやる、なかったらやらない。obj?.DoIt です。 |