見えないクラス群 – こたつつきみかん

逆に、.NET Frameworkでは中身を見せすぎなのではないかというきらいがあります。例えば、リストボックスの要素を読み書きするにはListBox.Itemsを使うのですが、こいつが返すのはListBox.ObjectCollectionというクラスだと記載されています。IListでは何がいけなかったのでしょうか。

話は逸れますがさらに1つ別の例を挙げます。こっちは、.NET Framework 1.0にジェネリックがなかったなど多少は仕方ない面もあることですが。


.NET Frameworkで、全てのファイルに対して何かを行うコードを書くとして、次のように書きたいとします。

foreach (fd in new FindFile("*.*"))
{
	//fdを参照する
}

これをWindows APIのFindFirstFile, FindNextFile, FindCloseで実装することを考えます。こう書けるようにするためには、FindFileのコンストラクタか、GetEnumeratorでFindFirstFile、MoveNext(2回目以降)でFindNextFile、DisposeでFindCloseを呼ぶようにすれば実現できます。そのようなコードは、あたかも次のように直接API関数を使うの同じ構造になります。

HFILE hFind = FindFirstFile("*.*", ref fd);
while (FindNextFile(hFind, ref fd))
{
	//fdを参照する
}
FindClose(hFind);

ところが、現実の.NET FrameworkのBCLの中でnew FindFile(“*.*”)の部分にあたるDirectory.GetFilesやGetDirectoriesなどは、配列を返します。つまり、直接APIを使うコードに展開すると次のようになります。

// これは擬似コード
List<WIN32_FIND_DATA> files;
HFILE hFind = FindFirstFile("*.*", ref fd);
while (FindNextFile(hFind, ref fd))
{
	files.Add(fd);
}
 
foreach (fd in files)
{
	//fdを参照する
}

このコードではバッファに蓄えるという構造に変形されてしまっています。もちろん、みんなが大多数の場合でバッファに蓄えるこういうコードを書いてきたというのなら、そういう風にクラスで包み込むのは適切かもしれません。しかし、最初の例ではFindFirstFileはforeachができれば十分です。そして、現実にもそういう場合が多々あったと思います。だから、Directory.GetFilesやGetDirectoriesはIEnumerable<T>を返して、最初に挙げたようなバッファを使わない実装だったら良かったのに、と思っています。

もちろん、バッファに蓄えたいときには必要に応じてそうできるようになっているのが望ましいです。幸いにも最新の.NET Framework 3.5にはEnumerable(IEnumerable<T>に非ず)にToListとToArrayがあります。それぞれIEnumerable<T>からList<T>とT型の配列を作成してそれを返す拡張メソッドです。

なお、この文章においてIEnumerable<T>とT型の配列の違いは、[]演算子でインデックスを指定して要素アクセスできるかどうかだと思って差し支えありません。

もちろん、これから作っていくABライブラリでは同じ轍を踏まないようにするぞと思っているわけです、少なくとも自分の書くコードでは。


2013年5月4日追記:.NET Framework 4では、上で書いたようなIEnumerable<T>を返すファイル列挙のメソッド、Directory.EnumerateFilesDirectory.EnumerateDirectoriesなどが追加されました。すばらしいです。


スポンサード リンク

この記事のカテゴリ

  • ⇒ 「歴史に『もし』はない」のではない、「もし」がないから歴史なのだ
  • ⇒ 「歴史に『もし』はない」のではない、「もし」がないから歴史なのだ