Array
クラスは Array
クラスは IList
インターフェイスを実装していますが, IList<T>
インターフェイスは実装しません。したがって次のコードは実行できないように思われます。
string[] array = new string[1]; IList list = (IList)array; IList<string> genericList = (IList<string>)array;
ところが実際に動かしてみると何のエラーも起こらずに普通に動きました。リフレクションでインターフェイスを調べてみると,ジェネリックリストのインターフェイスが実装されていたので,コンパイル時にそういうことが行われているんだろうと納得しました。というかちゃんと MSDN に書いてありました。しかも重要って書いてありました。
.NET Framework Version 2.0 では、 Array クラスは System.Collections.Generic.IList(T)、 System.Collections.Generic.ICollection(T)、および System.Collections.Generic.IEnumerable(T) の各ジェネリック インターフェイスを実装します。
反省しつつも,もう少し調べてみると不思議なことが起こりました。
Console.WriteLine(array.IsReadOnly); // False Console.WriteLine(list.IsReadOnly); // False Console.WriteLine(genericList.IsReadOnly); // True
上の 2 つは期待通りの動作をするのですが,最後の挙動は不思議です。じゃあ IList<string>
にキャストしたら本当に読み込み専用になるのかというとそうでもないようです。
genericList[0] = "hoge"; Console.WriteLine(array[0]); // hoge Console.WriteLine(list[0]); // hoge Console.WriteLine(genericList[0]); // hoge
でもやはりジェネリックか否かで IsReadOnly
の結果は違うのです。コレクションで実装されるかリストで実装されるかの違いが原因なのでしょうか。
// ICollection.IsReadOnly は存在しない。 // Console.WriteLine(genericList.IsReadOnly == ((ICollection)genericList).IsReadOnly); Console.WriteLine(genericList.IsReadOnly == ((IList)genericList).IsReadOnly); // False Console.WriteLine(genericList.IsReadOnly == ((ICollection<string>)genericList).IsReadOnly); // True
同じ EXE ファイル (Visual Studio でコンパイル) を mono で実行すると, genericList.IsReadOnly
で False
を返しました。これ妥当な結果を返していると思います。逆に mono でコンパイルした EXE ファイルを .NET Framework で動かすと,上で述べてきたような変な結果になります。つまりは .NET Framework ランタイムのバグということでしょうか。
問題になるのは IsReadOnly
で条件判定している場合ですね。
public void InitializeList<T>(IList<T> list) { if (list == null) { throw new ArgumentNullException(); } if (list.IsReadOnly) { throw new ArgumentException(); } for (int index = 0; index < list.Count; index++) { list[index] = default(T); } }
あまりあるケースだとは思いませんが,一応ジェネリックリストを引数にとるメソッドは引数が配列であるかどうかをチェックした方が良いかもしれません。