(鳥山 隆一=TRO)

著者紹介
 IT関連ライター。プログラミングから,IP電話,企業におけるシステム導入まで,IT関連で幅広く活躍。2003年に企業のシステム導入事例の作成を業務の中心とする有限会社TROを設立。IT関連企業の導入事例を多く手がけている。
 今回は前回に引き続きNullable型について説明しよう。簡単におさらいすると,Nullable型とはC#2.0の新機能の目玉の1つで,整数型や小数点型などの値型の変数でも文字列型などの参照型の変数と同じようにNull値を取れるようにしたデータ型である。データベースとの親和性を高めるために取り入れられた。もちろんデータベースと関係なく利用することもできる。そこで今回はNullable型を実際に利用するメリットを考えつつ,変数の比較と,Nullの代替値を返すための演算子について紹介する。

Nullable型の変数を使った比較演算
 ここではNullable型を,32ビット符号付き整数型(int型)を例に挙げて説明するが,ほかのビット数の整数型や浮動小数点型(float型やdouble型)などでも同様である。

 まずはNullable型の変数を初期化しよう。MyIntというNullable整数型変数を定義し,Nullを代入するコードは,前回説明したように,以下のようになる。

int? MyInt = null;

 変数の宣言方法に関して,Nullable型と非Nullable型の違いは,“?”を付ける点である。前回説明した値の代入について,簡単に復習しておこう。Nullable型変数に非Nullable型変数の値を代入するときには,暗黙の型変換が行われ,そのまま代入できる。例外は発生しない。非Nullable型変数に,Nullable型変数の値を代入するときは,キャストが必要で,Nullを代入しようとしたときだけ例外(実行時エラー)が発生する。

 さらに四則演算についても説明した。基本的に非Nullable型変数同士の演算結果と同等だが,演算する変数のどちらか一方もしくは両方がNullの場合,加算,減算,乗算,除算にかかわらず結果はすべてNullになる。除算の除数がNullであろうとも,結果はNullだ。


※Webブラウザによっては,リスト部分が正常に表示されない場合があります。その場合は,こちらのページをごらんください。

 上の処理の結果,MyNullableIntC1からMyNullableIntC4まで,すべてNullになる。このことを念頭に置いておいて,等値演算を考えてみよう。以下の演算結果はすぐに思いつくだろう。


※Webブラウザによっては,リスト部分が正常に表示されない場合があります。その場合は,こちらのページをごらんください。

 MyBool1とMyBool5がTrueで,MyBool2,MyBool3,MyBool4はFalseになる。Nullable型の値と非Nullable型の値との比較は,非Nullable型同士の等値演算に準じた結果になる。特に不自然に感じる部分はないはずだ。

 それでは今度は,比較演算をしてみよう。ここでは,先の四則演算の結果を思い出してほしい。Nullとの演算結果はすべてNullになった。比較演算は,これに似ている。


※Webブラウザによっては,リスト部分が正常に表示されない場合があります。その場合は,こちらのページをごらんください。

 実行するとMyBool2以外はすべてFalseになる。Nullable型の値と非Nullable型の値とを比較した場合,Nullable型変数の値がNullでなければ,非Nullable型同士の比較と同じ結果になる(上記リストの1と2)。しかし当然だが,Nullable型変数がNullの場合は,非Nullable型の変数の値が何であろうと結果はFalseになる(上記リストの3)。また,Nullable型変数とNull値との比較結果は,すべてFalseになる(上記リストの4と5)。このため,少し不自然に感じることが起こる。次のリストを見てほしい。

int? MyNullableIntA = 2;
int? MyNullableIntB = null;
bool MyBool1, MyBool2, MyBool3;

MyBool1 = MyNullableIntA > MyNullableIntB;
MyBool2 = MyNullableIntA < MyNullableIntB;
MyBool3 = MyNullableIntA == MyNullableIntB;

 実行すると,MyBool1,MyBool2,MyBool3はすべてFalseになる。つまり,MyNullableIntAは,MyNullableIntBよりも大きくなく,かといって小さくもなく,しかも等しいわけでもない,ということになる。値型の変数で,それまで自然に行っていた比較演算の排他的なロジックを,そのままNullable型に当てはめようとすると,こうしたところでバグが発生してしまう恐れがある。

 さらに,四則演算と等値演算を組み合わせた場合も注意が必要になってくる。例えば,非Nullable型の値では,“等しい”ことと“差がない”ことは同じことを意味した。だがNullable型では違う。次の式を考えてみよう。


※Webブラウザによっては,リスト部分が正常に表示されない場合があります。その場合は,こちらのページをごらんください。

 MyBool1とMyBool2は,MyNullableIntAとMyNullableIntBの値が異なっているので,当然2つともFalseになる。しかしMyNullableIntBとMyNullableIntCは,ともに値はNullで等しのだが,差は0にならない。つまりMyBool3はTrueになるが,MyBool4はFalseになる。

 NullからNullを引くと,結果は0ではなくNullになる。つまりMyBool5はTrueになる。しかし両方ともNullなのか,どちらか一方だけがNullなのか,判別することはできない。MyBool6もTrueになる。差を判別して得られる情報は,直接等しいかどうかを判別して得られる情報より少ないのだ。四則演算にNullが関係してくると,答えは常にNullになる。Nullable型が,非Nullable型とほとんど同じように使えるからといって,これまで行っていたプログラミングのロジックを,すべてそのままNullable型で使うことはできない。

Null値を置き換える
 C#2.0では,Nullableに関連する機能として,Null値を置き換える演算子(null coalescing operator)も追加された。それが“??”である。これは,簡単にいえば,対象とする変数の値がNullならばそれを特定の値に置き換えるもの。実際に使ってみよう。

int? MyNullableIntA = 2;
int? MyNullableIntB = null;

int MyInt1, MyInt2, MyInt3;
MyInt1 = MyNullableIntA ?? 0;//(1)
MyInt2 = MyNullableIntB ?? 0;//(2)
MyInt3 = MyNullableIntB ?? (int)MyNullableIntA; //(3)

 実行すると,上記リストの(1)では,MyNullableIntAがNullではないので,MyInt1にはMyNullableIntAの値である2が入る。同(2)では,MyNullableIntBがNullなので,MyInt2にはMyNullableIntBの値ではなく0が入る。また,同(3)のように,もちろん??は変数同士を指定できる。この場合,Nullの場合の値となる右側の変数にNullable型を指定するとコンパイル・エラーになるので,非Nullable型にキャストしておく必要がある。ちなみにMyInt3には,2が入る。

 次のように,複数個を組み合わせて使うこともできる。

MyInt1 = MyNullableIntA ?? (MyNullableIntB ?? (MyNullableIntC ?? -1));

 ??を使うことで,Nullable型と非Nullable型を切り替えてプログラミングできるようになる。四則演算や比較演算をする場合には,一度非Nullable型に置き換えた上で処理した方が間違えを減らせることもある。対象となる演算が,Nullをどのように扱うのか検討して,最適な値に置き換えることで,ミスのないプログラミングを目指してほしい。

 次回は,C#の言語仕様の強化点では最も大きいといえるジェネリクスについて取り上げる。利用方法や考え方,メリットなどを分かってもらえるだろう。