連載:[完全版]究極のC#プログラミング

Chapter2 ジェネリック

川俣 晶
2009/08/17

2.7 HashtableクラスとDictionaryクラスの非互換性

 ジェネリックコレクションは容易に使うことができ、すぐにでも従来のコレクションから移行したいものである。

 しかし、中には(従来版からジェネリック版への)機械的な置き換えでは対処できないものもある。特に深刻なのは、HashtableクラスとDictionaryクラスの非互換動作の例だろう。実際にコードを見ていただきたい(リスト2.9参照)。

using System;
using System.Collections;
using System.Collections.Generic;

class Program
{
  static void Main(string[] args)
  {
    try
    {
      // Hashtableクラス(従来のコレクションクラス)
      Hashtable hashtable = new Hashtable();
      Console.WriteLine(hashtable[0] == null);

      // Dictionaryクラス(ジェネリックコレクション)
      Dictionary<int, object> dictionary
                                 = new Dictionary<int, object>();
      Console.WriteLine(dictionary[0] == null); // 例外が発生
    }
    catch (Exception e)
    {
      Console.WriteLine(e.ToString());
    }
  }
}
リスト2.9 HashtableクラスとDictionaryクラスの非互換動作の例

True
System.Collections.Generic.KeyNotFoundException: 指定されたキーはディレクトリ内に存在しませんでした。
   場所 System.ThrowHelper.ThrowKeyNotFoundException()
   場所 System.Collections.Generic.Dictionary`2.get_Item(TKey key)
   場所 Program.Main(String[] args) 場所 ……\Program.cs:行 15
リスト2.9の実行結果

 このとおり、存在しないキーに対するアクセスは、従来のHashtableクラスではnullを返すが、新しいDictionaryクラスではSystem.Collections.Generic.KeyNotFoundException例外を投げる。

 あるキーに対する値が存在するか否かを判定する処理は比較的よくあるが、もし上記のサンプルコードのような方法で判定している場合は書き直す必要がある。あるキーがコレクションに含まれているかは、ContainsKeyメソッドで判定できる。また、キーの存在チェックと値の取得を1回で行う場合は、TryGetValueメソッドという便利なメソッドもある。これを用い、

if (Dictionaryオブジェクト.TryGetValue(キー, out value)) { xxxx }

といった文を実行すると、キーが存在するときにのみ変数valueに値が入り、if文の条件が成立するので、「xxxx」の部分にvalueの値を使うコードを安全に書くことができる。

 しかし、なぜこのような仕様変更が行われたのだろうか? よく考えると新しいDictionaryクラスの仕様は合理的であることがわかる。

 たとえば、C# 2.0の追加機能、「null許容型」(第5章参照)を使うと、値としてnullも代入できるint型などを容易に使うことができる。この型の値をコレクションに追加したとき、当然のことながら、nullも有効な値と見なされなければならない。

 これを実際に記述した例をリスト2.10に示す(「int?」はnull許容型のint型)。

using System;
using System.Collections.Generic;

class Program
{
  static void Main(string[] args)
  {
    Dictionary<int, int?> dictionary = new Dictionary<int, int?>();
    dictionary[0] = null;

    Console.WriteLine(dictionary.Count); // 出力:1
    Console.WriteLine(dictionary[0] == null); // 出力:True
  }
}
リスト2.10 nullが正規の値となるコレクション

 このプログラムは、コレクションに確かに1つのキーと値のペアを入れている。そのことは、dictionary.Countの値が1であることから確認できる。しかし、格納された値はnullである。

 もし、キーが存在しないときに得られる値もnullであれば、nullという値が格納されたのか、キーが存在しないのか、区別ができないことになる。キーがないときは例外を投げるという仕様は、この区別を明確にするために必要とされる対処というわけである。

 ただし、例外は重い処理なので、例外を使うよりも上述のContainsKeyメソッドやTryGetValueメソッドで判定するほうがよいだろう。


 INDEX
  [完全版]究極のC#プログラミング
  Chapter2 ジェネリック
    1.2.1 ジェネリックとは何か?
    2.2.2 新しいコレクションの紹介
    3.2.3 新しいコレクションクラス―LinkedListクラス
    4.2.4 新しいコレクションクラス―SortedDictionaryクラス
    5.2.5 ジェネリックコレクションの使い方
    6.2.6 ジェネリックメソッドと型推論
  7.2.7 HashtableクラスとDictionaryクラスの非互換性
    8.2.8 ジェネリックなクラスを自作する
    9.2.9 制約の付いたジェネリックなクラス/C++のtemplate機能との相違/練習問題
 
インデックス・ページヘ  「[完全版]究極のC#プログラミング」


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 記事ランキング

本日 月間