【.NET Framework 4.5】IReadOnlyListとIReadOnlyDictionary
- 2013.02.07 Thursday
- 21:28
.NET Framework 4.5からIReadOnlyList<out T>とIReadOnlyDictionary<TKey, TValue>が追加されたようですね。
IReadOnlyList<out T>がインデックスをサポートする読み取り専用のコレクション。
IReadOnlyDictionary<TKey, TValue>がキーと値のペアの読み取り専用ディクショナリ−。
class A
{
private readonly List<string> _list = new List<string>();
public void HogeAction()
{
// 内部では_listを可変長で要素の入れ替えが可能なListとして扱うロジックが書ける。
}
// 利用する側はインデックスがサポートされた読み取り専用のコレクションとして扱う事ができる。
// Aクラスの内部状態に不用意に干渉することを避けられる。
public IReadOnlyList<string> Source { get { return _list; } }
}
IList<T>は、読み取り専用も可変長もプロパティで利用側が判断する必要があり、なかなか使いにくく、隠蔽化されたオブジェクトからメソッドやプロパティでIList<T>が返された場合は、なるべく触らぬ神にたたり無しと入った感じで、暗黙的にIReadOnlyList的な使い方をしていました。もしくは、新しくコレクションを作成していました。そういった気苦労をしなくて良くなったという訳です。
またクラス提供者としては、メソッドやプロパティでIReadOnlyListを公開し、その内部ではList<T>として扱う事ができ、可変長で値の変更も自由にできる、外部ではそのインスタンスを読み取り専用としてそのまま公開できるというメリットが生まれます。インデックスをサポートしてないだけで.NET Famework 3.5(だったかな?)でIReadOnlyCollection<T>が追加されていたので、インデックスが使えるようになったというところが良くなった点ですが・・・・。
IReadOnlyCollection<T>と同様に、IReadOnlyList<T>のTのインスタンスの状態はTクラスの設計次第で可変になるのでこの当たりは注意が必要かと・・・・心配ならTクラスに対しても読み取り専用TReadOnlyを用意し、ジェネリクスの共変性を利用すれば完全に書き換え不可能なものを返す事もできそうですが・・・めんどくさいですね。
ジェネリクスの共変性を利用して、要素の状態も書き換え不可能なコードを作成してみましたが、これはとにかくめんどくさいですね。
static void Main(string[] args)
{
var list1 = new List<T> {new T() {Value = 1}, new T() {Value = 2}};
list1[1].Value = 10; // 書き換え可能
IReadOnlyList<T> list2 = new List<T> { new T() { Value = 1 }, new T() { Value = 2 } };
list2[1].Value = 10; // 書き換え可能
IReadOnlyList<IReadOnlyT> list3 = new List<T> { new T() { Value = 1 }, new T() { Value = 2 } };
// list3[1].Value = 10; // 書き換え不可能コンパイルエラー
}
interface IReadOnlyT
{
int Value { get; }
}
class T : IReadOnlyT
{
public int Value { get; set; }
}
----------------------------------------
今日、さんざんこのインターフェイスを使って、コードを書いてみたのですが、すごくしっくりくるコードが書けました。
----------------------------------------
IReadOnlyDictionaryは、Linq to Objectsを使っているとToDicitonaryメソッドではキーがぶつかった時に例外が発生してしまうため、あまり使う機会がなくなりました。代わりにILookupをよく使うようになりました。HashSetの読み取り専用もあっても良いかもしれません。私が利用する場合はHashSetはほとんどコンストラクタ時に決定したコレクションから変動することがないので・・・。といってもローカル変数レベルの話なので、いらないのか・・・・保守の観点から同じクラスを複数人がいじるときのコミュニケーションとして必要?