BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ ニュース .NETのリードオンリー コレクションインターフェースの物語

.NETのリードオンリー コレクションインターフェースの物語

原文(投稿日:2011/10/18)へのリンク

.NET 4.5では、2つの新しいインターフェース、 IReadOnlyList と IReadOnlyDictionaryが追加された。これらは、表面的には至って控えめだが、後方互換性、相互運用性そして、共変の役割についてのかなり複雑な話が見えてくる。

IReadOnlyList と IreadOnlyDictionaryは、.NETの本当の初期から開発者が望んでいたインターフェースである。対称性を与える他に、リードオンリーなインターフェースは、NotSupportedExceptionを投げるだけのメソッドを実装する必要がなくなる。理由は不明だが、盛り込まれなかった。

次のチャンスは、.NET 2でジェネリックスが導入された時だった。これによって、Microsoftは、弱く型づけされたコレクションやインターフェースを徐々に廃止して、強く型づけされた同等のもので置き換えていくことができた。 Base Class Libraryチームは、再びリードオンリー リストを提供する機会を逃した。Kit George氏が書いている

我々は、Joeさん、あなたが望んでいるもののために、既定の実装を提供することができたので、インターフェースでなく、 ReadOnlyCollectionBaseを提供しました。しかし、もしそれが強い型づけされていなかったら、それを使う気がしないでしょう。しかし、ジェネリックスの導入により、ReadOnlyCollection<T>を持てるようになったので、同じ機能が手に入ります。しかし、強い型づけされています。素晴らしい!

ReadOnlyCollection<T>は、 sealedされてませんので、必要であればお好きなコレクションをその上に書くことができます。我々は、これと同じ概念のインターフェースを導入するつもりはありません。我々が作ったコレクションは、一般的な用途に合うものだからです。

Krzysztof Cwalina氏もこの件について意見を述べた。

驚かすように聞こえるかもしれませんが、 IList も IList<T>もリードオンリー コレクションを意図したインターフェースです。これらは、 IsReadOnly Booleanプロパティを持ち、これはリードオンリー コレクションによって実装された時はtrueを返すべきです。我々が純粋のリードオンリーインターフェースを追加したくない理由は、そうするとライブラリに余りにも不要な複雑さを持ち込むと思ったからです。注意して欲しいのは、複雑さは、新しいインターフェースとその使う方の両方にです。

我々は、API設計者は実行時に IsReadOnlyプロパティをチェックし、IListが問題ない場合に、潜在的に例外を投げることを気にかけていないか、あるいは、明示的にIList を実装し、カスタム化したリードオンリーAPIを公開する場合には、本当にきれいなAPIを提供したがる、と考えています。後者の典型は、フォームオブジェクトモデルを公開しているコレクションです。

開発者がこの状況について不満を言っていたが、ジェネリックスによって与えられた新しいチャンスは、この障害を完全に押し流し、このことは、.NET 4までほとんど無視された。しかし、この決定には波紋もあり、後で述べることにする。

.NET 4 では、興奮するような新しい機能がランタイムに追加された。以前の.NETバージョンでは、インターフェースは、型に関しては、非常に制限されていた。例えば、Customer がPersonからの継承クラスでも、IEnumerable<Person> を期待している関数にパラメータとしてIEnumerable<Customer> 型のオブジェクトを使うことができなかった。 共変のサポートが追加されたことで、この制限が部分的になくなった。

「部分的に」と言っているのは、 IEnumerableよりリッチなAPIを持つインターフェースを使いたくなる、いくつものシナリオがあるからである。 IList は共変でないが、リードオンリーリスト インターフェースは共変にできたろう。不幸にして、.NET BCLチームは再びこの見落としに対処しないことを決めた。

その後、WinRTの導入とCOMの復活が全てを変えた。COMの相互運用性はかつて、他に選択肢がない時に開発者が使うものだった。しかし今では、.NETプログラムの基礎である。そして WinRTがインターフェースの IVectorView<T>IMapView<K, V>を公開するので、.NETもそうしなければならない。

WinRT計画の非常に面白いフィーチャは、それぞれの開発プラットフォーム用に、違っているが、似たようなAPIを公開していることである。もう既にご存知だろうが、JavaScriptの開発者から見える全てのメソッドは、キャメルケース化されており、 C++ と .NET開発者はパスカルケース化されたメッソドを見ることになる。また、もっと劇的な変化は、C++と .NET間のインターフェースの自動マッピングである。.NET開発者は、Windows.Foundation.Collectionsネームスペースを使うのではなく、 System.Collections.Genericを使い続けるだろう。インターフェースのIVectorView<T> と IMapView<K,V> は、ランタイムによって IReadOnlyList<T>IReadOnlyDictionary<TKey, TValue>に翻訳される。

C++/WinRT用のインターフェース名は、幾分もっと正確であることに注目することは重要である。これらのインターフェースは、コレクションへのビューを表現することを意図されているが、コレクション自身がイミュータブル(不変)であることは保証していない。経験のある.NET開発者でさえ良く間違えるのは、ReadOnlyCollectionがコレクションのイミュータブルなコピーである、と考えてしまうことである。実はそれは実際のコレクションのラッパーに過ぎない。( リードオンリー、フロウズン、イミュータブルなコレクションについて更に知りたければ、 Andrew Arnott氏による同名の投稿を見て欲しい)。

面白いのが、IList<T>は、IReadOnlyList<T>から継承していないことである。全く同じメンバーを持ち、全リストがリードオンリーリストとして表現できるにもかかわらずである。Immo Landwerth氏の説明では、

リードオンリーインターフェースは、純粋に read-writeインターフェースのサブセットだから上手く機能している、と考えるのは筋が通っているように思えます。不幸にして、非互換です。メタデータレベルでは、あらゆるインターフェースのあらゆるメソッドは、自身のスロット(このために明示的なインターフェース実装が上手くいく)を持っているからです。

言い換えると、リードオンリーインターフェースをミュータブルな型のベースクラスとして導入する唯一のチャンスは、元々考えついた.NET 2.0の時だった。一旦世にリリースされれば、それにされたであろう変更は、共変と反変の印を追加することだけだった(VB と C#で“in” と “out”として表現されている)。

なぜIReadOnlyCollection<T>がないのか尋ねると、 Immo氏が答えた。

我々はその設計を考えたのですが、 Countプロパティしか提供しない型を加えるのは、BCLにそれ程の価値を加えるとは思えませんでした。BCLチームでは、我々は、APIというものはマイナス1,000点から始まり、ちょっとぐらい価値があっても追加するのを正当化するほど充分は良くない、と考えています。理由は、新しいAPIを追加するにもコストがかかるからです。例えば開発者は、もっと多くの概念から選ばなくてはならなくなります。最初は、この型を追加すれば、ただカウントだけを取得して、それで何か面白いことをやるようなシナリオおいては、パフォーマンスが上がるだろう、と考えました。例えば、既存のコレクションに大量に追加する時に。しかしこのようなシナリオでは、我々は既に、単にIEnumerable<T> を使い、特別な場合にはインスタンスにICollection<T>を実装することも薦めています。組込みのコレクション型の全てがこのインターフェースを実装しているので、ほとんどの共通のシナリオではパフォーマンスは上がりません。ついでながら、IEnumerable<T> のCount()拡張メソッドもこうします。

新しいインターフェースは、.NET 4.5と Windows 8用の.NETで使える。

この記事に星をつける

おすすめ度
スタイル

BT