スポンサーリンク

2010年6月26日土曜日

CollectionBase とジェネリック

 オブジェクト指向プログラミングをしていると、厳密に型指定された配列やリストを扱いたい場合がある。単純なインスタンスとして使いたいのであればList<T>のようなジェネリックでも問題ないと思うんだけど、昔リストを内包するクラスのソースを見ていて、そのリストがCollectionBaseクラスを継承して書かれていることに気付いた。

 早速MSDNライブラリで検索してみる。


CollectionBase クラス (System.Collections)

厳密に型指定されたコレクションの abstract 基本クラスを提供します。


 うん、あっそう、って感じ。

 ただ、CollectionBaseクラスは基本的にObjectクラスを扱うようになっているので、扱いたい型を使っていくつかのメソッドをオーバーライドしなきゃならない。もちろん、必要なメソッドだけオーバーライドするのもいいけど、オーバーライドしないメソッドはやはり対象はObjectクラスのまま。気持ち悪い。とは言え、全部のメソッドをオーバーライドするのもかったるい。めんどい。

 この手間を省くのにジェネリッククラスを継承してコレクションを作るやり方がある感じ。でも、そうするとどのジェネリッククラスを継承するのかという別の問題も出てくる。Collection<T>とかList<T>とか。それこそ継承するんじゃなくて、List<T>クラスをコンポジションして、必要なインタフェースだけ提供するというのもあり。これだとインタフェースまで限定できる。まあ、これは設計次第。

 話を戻して。じゃあ、どのジェネリッククラスを継承したらいいのか。いろいろとグーグル先生に聞いていたら気になる記事を発見。


LINQifying - getting rid of CollectionBase? - Rick Strahl’s Web Log


 これによると、ただ厳密に型指定されたコレクションを作りたいのであれば、List<T>を継承したクラスを作れば良さそう。ただList<T>クラスにはオーバーライド可能なメソッドがないので、独自の拡張を実装できない。なるほど。

 このようなコレクションを独自に作るということは、厳密に指定する型が独自クラスの場合が多い。そうなると当然、ソートなどの比較方法が単純な大小で行えるものではないので、ソート部分も独自に実装しなくちゃならない。こうなるとList<T>クラスの継承では無理。そこで次に考えるのがCollection<T>クラスの継承を継承すること。ソート処理以外の部分はCollection<T>が肩代わりしてくれてるので、ソート処理だけを追加すればいい。

 さらにさらに、普通のコレクションとしての機能も拡張する必要が出てきた場合。この場合にCollectionBaseクラスを継承する。ただ、これだけだと反復処理に対応しないので、必要ならばIEnumerable<T>インタフェースも実装するよろし。

 ここまでで大体どの状況でどのクラスを継承するべきかが見えてきた。ポイントは拡張の必要性かな?

 拡張の柔軟性の順に選ぶとしたら以下のような感じで。

CollectionBase ⇒ CollectionBase + IEnumerable<T> ⇒ Collection<T> ⇒ List<T>

 今まで気分で使い分けてただけに調べて良かった、すっきりした。

 もし間違って解釈していたら指摘頂ければと思います。