連載:C# 2.0入門

第4回 Findメソッドとnull許容型

株式会社ピーデー 川俣 晶
2007/08/31

is演算子の挙動に注意

 is演算子も、null許容型に対してストレートではない挙動を示す。

using System;

class Program
{
  static void Main(string[] args)
  {
    int? a;

    a = 123;
    Console.WriteLine(a is string); // 出力:False
    Console.WriteLine(a is int);    // 出力:True
    Console.WriteLine(a is int?);   // 出力:True

    a = null;
    Console.WriteLine(a is string); // 出力:False
    Console.WriteLine(a is int);    // 出力:False
    Console.WriteLine(a is int?);   // 出力:False

    Console.WriteLine(123 is string); // 出力:False
    Console.WriteLine(123 is int);    // 出力:True
    Console.WriteLine(123 is int?);   // 出力:True
  }
}
リスト15 is演算子を適用した例

 これを見ると分かるとおり、null許容型として宣言された変数aに整数を入れた場合、「a is int」と「a is int?」の双方がtrueになる。それどころか、純粋な整数(int型)の定数123に対してすら、「123 is int」と「123 is int?」の双方がtrueになる。

 一方、nullを入れた場合、「a is int」と「a is int?」の双方がfalseになる。ちなみに、nullが入っているとfalseになるのはis演算子の基本的な仕様で、ほかの参照型を相手に使っても同じようにfalseになる(「a is string」がfalseになっているのはそのため)。

 ここで確認しておく価値があることは、is演算子は「int?」という型に対して、一般の型とは違った挙動を示すことである。明らかにint型であって、int?型ではない定数123に対して「123 is int?」がtrueになるというのは、変換の整合性を維持するためだろう。

 定数123はそのままint?型の変数に代入できるわけで、それは暗黙的にint?型に変換可能ということを意味する。だから、「123 is int?」はtrueであった方が使いやすいのだろう。

 ちなみに、値型には使用できないas演算子もnull許容型で使用できるよう拡張されている。

3値論理型として使用できるbool?型

 通常、論理型はtrue/falseという2つの値のいずれかを取る。それ故に、trueでなければ必ずfalse、falseでなければ必ずtrueとなる。

 よって、以下の2つの文は等価だと見なせる。

  • if ( 式 ) { 文1; } else { 文2; }
  • if ( !式 ) { 文2; } else { 文1; }

 ところが世の中には、2つではなく3つの値で表す「3値論理型」が存在する。有名なものには、SQLの論理型がある。

 さて、nullを許容するbool?型は、true、false、nullという3種類の状態を持つことができるので、3値論理型として使用できる。そこで、bool?型に対する「&」と「|」の演算子は、SQLと互換性のある以下のような結果を出すようになっている。

x y x & y x | y
true true true true
true false false true
true null null true
false true false true
false false false false
false null false null
null true null true
null false false null
null null null null
bool?型における「&演算」と「|演算」の演算結果

 それ故に、本来はできないはずのtrueとnullに対する&演算などが実行できる。

 リスト16は実際にそれを行った例である。「a & b」は計算可能であり、変数cの値はnullになる。よって、このプログラムは「c == null」が成立するのでtrueを出力する。

using System;

class Program
{
  static void Main(string[] args)
  {
    bool? a = true, b = null;
    bool? c = a & b;

    Console.WriteLine(c == null); // 出力:True
  }
}
リスト16 3値論理型として使用したbool?型

nullを許容するとパフォーマンスに影響するか?

 null許容型を使用すると、パフォーマンスに差が出るだろうか。

 まず、必然的にメモリ使用量が増えることを確認しておこう。nullであるか否かという情報を記憶する分だけ、メモリを余計に消費せざるを得ないのである。

 では速度面ではどうだろうか。簡単な比較プログラムを作成した。

using System;

class Program
{
  static void Main(string[] args)
  {
    int sum1 = 0;
    DateTime start1 = DateTime.Now;

    for (int i = 0; i < int.MaxValue; i++)
    {
      sum1 += i;
    }
    Console.WriteLine(DateTime.Now - start1);

    int? sum2 = 0;
    DateTime start2 = DateTime.Now;

    for (int i = 0; i < int.MaxValue; i++)
    {
      sum2 += i;
    }
    Console.WriteLine(DateTime.Now - start2);
  }
}
リスト17 null非許容型とnull許容型の速度比較

00:00:01.0430000
00:01:43.9360000
リスト17の実行結果例

 見てのとおり、けたが1つも違う結果になった。

 つまり、null許容型とは、相当の性能低下を許容したうえでなければ使用できない型ということになる。だから、本当にnullを許容する価値がある場合にのみ使用すべきだろう。nullになることが絶対にあり得ない変数の型に使うべきではない。

補足:null許容への批判

 一応、補足的に付け加えておこう。

 値型であってもnullが使えるようになることは、必ずしも全面的に肯定できるものではない。なぜかといえば、nullを使うべきではない……という主張も実際に存在するためである。

 最も典型的なものは、リファクタリング・カタログに存在する「nullオブジェクトの導入」だろう。これは、null値が存在することによってnullのケースの処理を行うためのコードが肥大化し、バグが入り込みやすくなるという問題への処方せんとなるものである。

 この場合、変数や引数にnullを代入する代わりに、何もしないオブジェクトである「nullオブジェクト」を代入する。nullオブジェクトをうまく活用すると、ソースがより簡潔になり、バグも出にくくなる。このようなメリットを享受していると、nullの利用を拡大することに抵抗感が出る可能性があり得る。

 また、RDBの世界にも、NULLを使うべきではないと主張する意見がある。

 このような批判に加え、速度が圧倒的に遅いという状況を加味すれば、null許容型は素直に喜んで受け入れられるものではないだろう。

 しかし、NULLを含むRDBのデータを処理するプログラムを簡潔に記述する……ということになれば、話は変わる。データベースに格納された値をストレートに受け止められる変数を宣言できれば、それはソース・コードが簡潔になることを意味する。それは大いにメリットのあることだろう。

次回予告

 次回はお待ちかね、ついに匿名メソッドとデリゲートについて語るときがきた。匿名メソッドを使うと、いかにソース・コードの質が変わってしまうのか。

 その片りんは今回のFindメソッドなどの話題でも見ることができたが、まさにソース・コードの書き方の基本骨格が変わってしまう、とても大きなトピックなのである。End of Article


 INDEX
  C# 2.0入門
  第4回 Findメソッドとnull許容型
    1.MATステートメントの思い出/前回に語り残したこと:ForEachメソッドのbreak問題
    2.ForEachだけではない繰り返しメソッド/複数の結果が欲しい場合/偉大なる前進とは何か?/そしてC# 3.0とLINQへ続く
    3.null許容型とは何か?/なぜnullを入れたいのか
    4.null許容型の内部構造/null合体演算子
  5.is演算子の挙動に注意/3値論理型として使用できるbool?型/nullを許容するとパフォーマンスに影響するか?/補足:null許容への批判
 
インデックス・ページヘ  「C# 2.0入門」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間