Nullableはまあまあ賢い
はじめに
System.Nullable<T>は、まあまあ賢い。まあまあ賢いとはどういうことかというと、予想したとおり動作してくれる。具体的には、
- nullとの比較
- ボックス化
が、予想したとおり動作する。
サンプルコード
以下に、NUnit(バージョン2.5以上)でのテストコードを示す。このテストがパスする。
int? x = null; int? y = null; // x, y とも null Assert.IsTrue(null == x); // null との == 演算子 Assert.IsFalse(null != x); // null との != 演算子 Assert.AreEqual(null, x); // null との Equals メソッド Assert.IsNull(x); Assert.IsTrue(x == y); // Nullable 同士の == 演算子 Assert.IsFalse(x != y); // Nullable 同士の != 演算子 Assert.AreEqual(x, y); // Nullable 同士の Equals メソッド // null をボックス化 object obj = x; Assert.IsNull(obj); // ボックス化結果が null x = (int?)obj; // エラーなしでボックス化解除可能 // ボックス化解除時の注意 1 long? longX; longX = (long?)obj; // obj が null だとボックス化解除可能 y = 1; // x が null で y が not null Assert.IsFalse(null == y); // null との == 演算子 Assert.IsFalse(x == y); // Nullable 同士の == 演算子 Assert.IsTrue(x != y); // Nullable 同士の != 演算子 Assert.AreNotEqual(x, y); // Nullable 同士の Equals メソッド // not null をボックス化 obj = y; Assert.IsNotNull(obj); // ボックス化結果が not null y = (int?)obj; // ボックス化解除時の注意 2 Assert.Throws(typeof(InvalidCastException), () => longX = (long?)obj); // obj が not null だとボックス化解除不可能 longX = (int?)obj; // これなら OK longX = (int)obj; // これも OK x = 1; // x, y とも not null かつ Value が等しい Assert.IsTrue(x == y); // Nullable 同士の == 演算子 Assert.IsFalse(x != y); // Nullable 同士の != 演算子 Assert.AreEqual(x, y); // Nullable 同士の Equals メソッド x = 2; // x, y とも not null かつ Value が等しくない Assert.IsFalse(x == y); // Nullable 同士の == 演算子 Assert.IsTrue(x != y); // Nullable 同士の != 演算子 Assert.AreNotEqual(x, y); // Nullable 同士の Equals メソッド
サンプルコードの解説
nullとの比較については、注意すべき点は何もないと思う。
ボックス化については、少し注意が必要だ。
ボックス化
Nullableをボックス化した場合、ボックス化結果のobjectがどのような値となるか、Nullable<T>.HasValue値毎に整理すると、
- falseの場合:object型の値null
※ボックス化前の型情報が、失われる - trueの場合:Nullable
.Valueを値とするobject {T}型の値
※ボックス化前にNullableだったという情報が、失われる
となる。
ボックス化解除
ボックス化解除は原則、ボックス化前の型へのボックス化解除でないと成功しない。しかし、ボックス化解除後の型がNullableの場合、少し異なる。サンプルコード内「ボックス化解除時の注意 2」のあたりを参照されたい。
上記のボックス化の仕様により、int? x = 1としたxをボックス化した結果は、object {int}の値1だ。この時点でボックス化前の型がint?だった情報は失われている。このことと、上記のボックス化解除の原則を組み合わせて考えると、intへのボックス化解除しか成功しないことになる。しかし、実際にはint?へのボックス化解除も成功する。まぁ、そういうことなのだろう。
ちなみに、longやlong?へのボックス化解除は失敗する。これは、ボックス化解除の原則どおりなだけである。