ページ

2005年8月10日水曜日

Nullable Boxing

TechEd 2005 Yokohama の初日(8月2日)の 「L3-1 C# 2.0 and IDE Tips & Tricks Part1」 のセッションで言われていたことです。スピーカーの波村さんによると 「今日から公開していいと言われた内容です。このデザインチェンジが行われたのは、ここ 1ヵ月半くらいのことです」 ということです。


で、どういったデザインチェンジかというと、
 void Foo<T>(T t)
 {
   Console.WriteLine("Foo<T>: {0}", t == null);
 }
 void Test()
 {
   int? i = null;
   Foo(i);
   Console.WriteLine("i == null: {0}", i == null);
   Console.WriteLine("(object)i == null: {0}", (object)i == null);
 }
これを実行するとどうなるか?という部分です。


実際に beta2 で実行すると
 Foo<T>: False
 i == null: True
 (object)i == null: False
となります。



  • 最初の Foo<T> では T は当然 int? となっています。問題は t == null の部分ですが、この部分の JIT による解釈は 「int? は値型の一種。値型は null には成り得ない。だから常に false」 ということになるそうです (波村さんに聞いたらこんな風におっしゃってたと思います)。int? は Nullable<int> ですし、Nullable<T> は struct ですから、「値型の一種」 というより値型そのものですね。だから null にならないのは当然とも言えます。(と思う。こういう解釈であってるのかな?)

  • 2番目の i == null は C# コンパイラが i.HasValue を呼び出すべきということを判断できます。なので、そのような IL が出力されます(実際に ildasm で見てみると i.HasValue を呼び出しているのがわかります)。

  • 最後の (object)i == null も Foo<T> と同じような感じで i を box 化して null と比較する IL が生成されます。だからこれは常に false です。

これはこれで正しいわけですが、やはりわかりにくいしバグの元でもあるので 「なんとかしようよ」 ということになったそうです。どうなんとかするかと言うと、「nullable を box 化したときに元が null であればちゃんと null になる(null と比較したとき true になる)」 となるようにしたってことでした。これによって、
 Foo<T>: True
 i == null: True
 (object)i == null: True
となることになります。


あと、
  int? i = null;
  object o = i;
  Console.WriteLine("o is int: {0}", o is int);
  Console.WriteLine("o is int?: {0}", o is int?);
の結果が
 o is int: True
 o is int?: True
となるようにも変更されるとのことでした。
beta2 では
  int? i = 1;
  int x = (int)i;
というコードをコンパイルできます。これは C# コンパイラによって
  int x = i.Value;
と解釈されています(IL を見るとそうなってます)。さて、それでは、
  int? i = 1;
  object o = i;
  int x = (int)o;
はどうでしょうか?これは beta2 では実行時エラーが発生します。o に格納されているのは box 化された int? なので int にキャストすることはできません。これを実行可能にするには、
  int x = (int)(int?)o;
のように int? にキャストしてから int にキャストしてやる必要があります。結局、int? を直接 int にキャストすることはできるのに、いったん box 化すると int にキャストすることはできなくなる、というふうに見えることになります。これが上記のように 「int? を box 化したものは int? でもあり int でもある」 となれば問題なく
  int? i = 1;
  object o = i;
  int x = (int)o;
というコードを実行できることになります。。。ということであってるのかなぁ?


まだ実際の処理系がないので、いまいち合ってるのかどうか自信はありませんが、こんなような感じみたいです。

0 件のコメント:

コメントを投稿