Generic Dictionary Classとインデクサ
.NET2.0におけるC#2.0で導入されるGenerics Collectionではキーと値の型を前もって指定できるDictionaryインタフェースと実装クラスが提供されており以下のようにインスタンスを生成することができます。
IDictionarydisposableTable = new Dictionary ();
これで変数disposableTableはキーにstring、値にIDisposableインタフェースを実装したオブジェクトだけを格納することができるhomogenouseな辞書として使用します。
仕事で.NET1.1で書いたライブラリィをちょこちょこと.NET2.0に移行して試しているのですがインデクサの振る舞いに関してちょっと気になっています。インデクサといえば自身を示す識別子(this)で自動的に参照されるプロパティであり配列のように[](ブラケット)でアクセスするためにこの名前が付いています。
当然Genericな辞書クラスにもインデクサはあり、以下のように使うことができます。
IDisposable disposable = disposableTable[key]; if ( disposable != null ) { 〜 要素が見つかった時の処理 〜 }
.NET1.1におけるDictionary系のクラス(Hashtable)等でインデクサを使ったことがある方であればGenericであるが故にキャストが不要になっている以外にこのコードは見慣れたものだと思います。しかし、このコード.NET2.0(C#2.0)ではインデクサの参照時に変数"key"に結びつけられたIDisposableが存在しない場合に例外"KeyNotFoundException"をスローするのです。
従って.NET1.1で上記のようにインデクサを使用してアクセスしていた辞書をGenericに書換えた場合、存在チェックのコードは以下のどちらかのように書換える必要が出てきます。
- ContainsKeyを使用する
if ( disposableTable.ContainsKey(key) ) { 〜 要素が見つかった時の処理 〜 }
- TryGetValueを使用する
IDisposable disposable = null; if (disposableTable.TryGetValue(key, out disposable )) { 〜 要素が見つかった時の処理 〜 }
私自身Javaに慣れていることもありインデクサで存在チェックを兼ねることは殆どなく通常はContainsKeyを使用していました。TryGetValueは新たに導入されるチェック方法でありチェック後に辞書にもう一度アクセスする処理を省ける効率の良い選択肢です。※1
なぜ互換性をあっさり捨てて例外をスローするように振る舞いを変えたのでしょうね。辞書をGenericへ移行するという時点で修正は覚悟しているのですから存在チェックのロジックも諦めてTryGetValue等に書き直せば良いのですが例外をスローするようになったのが気になるんですよね。
※1:三項演算子と組み合わせるとこんなロジックが
if ( disposableTable.ContainsKey(key) ) { return disposableTable[key]; } else { return null; }こんな風にかける。
IDisposable disposable = null; return (disposableTable.TryGetValue(key, out disposable)) ? disposable : null;三項演算子万歳!