なんだかぱっとしないタイトル。
ここで言う「インターフェイス」とは、多重継承できるあの interface じゃなくて、もっと一般的な意味でのそれです。約束事、みたいな。
例えば、メソッドの引数に何かコレクションを受け取るとして、その型はどう宣言すべきか、と。
IEnumerable<T> か、ICollection<T> か、IList<T> か、はたまた T[] か。
極めて単純無思考には T[] 。
しかし、T[] のほとんどの特性は IList<T> も備えているため、俺は個人的に IList<T> を好む傾向にある。
多次元配列なんかは、もはや配列をそのまんま使うんじゃなくて、何らかのクラスを作ったほうがいいと思う。
しかし、ランダムアクセスの必要が無ければ、IEnumerable<T> でも十分ではある。
そういう時、IEnumerable<T> とすべきか否か。
ちょっと引っかかるのは、ICollection<T> や IList<T> は無限リストを許容しないが、IEnumerable<T> は許容するというところ。
ということは、IEnumerable<T> を引数に取るメソッドは、すべからく無限リストに対応すべし、ということになるか?
遅延処理を意図したロジックならいいが、そうでない場合もあるだろう。
IEnumerable<T>.Count() なんかは ICollection<T>.Count でもよかったんじゃないだろうか。無限リストに使ったらオーバーフローしたぞ。
まぁ、ICollection<T> や IList<T> を受け取ったとして、要素数が Int32.MaxValue 個ある場合にちゃんと処理できなければならない、というのもなかなか酷ではあるのだが。
また、これは感覚的な問題でもあるが、用途としては IEnumerable<T> で十分であっても、IList<T> を使いたいという心情もあると思う。
ちょっと前の var 論争でもあった意見だが、「IEnumerable<T> ではコレクションのような気がしない。IList<T> ならコレクションであるという意図が込められる」みたいな。
また別問題ではあるが、独自のデータ構造を作るか否かという問題もある。
例えば、Pair<T, U> という既存の型があった場合、あらゆる2値の組み合わせは Pair<T, U> で事足りるか、ということ。
Pair<T, U> は、それぞれの値の意味を理解しないので、.First とか .Second とかいうメンバ名にならざるを得ない。
人の名前を表すのに、FirstName と LastName というメンバを持つクラスを作るか、Pair<string, string> で済ませるか、とか。
独自のデータ型を作らずに既存の型を最大限活用した場合、
- メンバの意味がわかりにくくなる
- ジェネリックの入れ子が深くなる
という問題が発生するわけだが、どうよ? という。