[C#]LINQが使えないコレクションでもLINQしたい

LINQほど革命的な機能は無いと思ってる私です。Fluent Interfaceでラムダ式、もう何も言うことありません。

私はC#でコーディングするときは何でもかんでもLINQで処理しちゃうのですが、でも、ちょっと設計思想が古いライブラリを使うときなどはジェネリクスのIEnumerable<T>が実装されていないコレクションをよく目にします。そもそもそういう古いライブラリを使いたくないのですが、業務上、使わないといけない場面があり、理想論ばかりも言ってられません。

例示しましょう。たとえば、DataTableなんかが良い例です。今だったらEntity Frameworkを使うのがベストだと思いますが、DataTableを使っているシステムもいっぱいあるでしょう。そのとき、列の型がstringだったならば、MaxLengthに10を入れる、なんて処理を書きたい場合、普通はこうします。

DataTable table = createExampleTable();
for (int i = 0; i < table.Columns.Count; i++)
    if (table.Columns[i].DataType == typeof(string))
        table.Columns[i].MaxLength = 10;

私は、なんかこのforループがすごい嫌。だってやりたいのはコレクションをfor eachしたいだけで、Columns.Countと比較したり、iをインクリメントしたりしたいわけではないのです。

だから、こう書いてみる。

DataTable table = createExampleTable();
var cols = Enumerable.Range(0, table.Columns.Count).Select(i => table.Columns[i]);
foreach (var col in cols.Where(c => c.DataType == typeof(string)))
    col.MaxLength = 10;

こう書くと、foreach以降はやりたいことが明確にわかるが、2行目が超絶見にくい。じゃあ2行目を拡張メソッドとして定義したらいいんじゃない?と思い、

public static IEnumerable<T> AsEnumerable<T>(this System.Collections.IEnumerable collection)
{
   foreach (var e in collection)
       yield return (T)e;
}

こういう拡張メソッドを定義してみる。そうしたら、こういうふうに書ける。

DataTable table = createExampleTable();
foreach (var col in table.Columns.AsEnumerable<DataColumn>().Where(c => c.DataType == typeof(string)))
    col.MaxLength = 10;

いちいち型を指定しなければならないのが面倒であるが、これは最初に比べるとずっとわかりやすいコードで良いと思う。

でもこの方法もベストというわけではなくて、それはすなわち、コレクションが非ジェネリクスのIEnumerableを実装している必要があるということである。私はGrapecity社が出しているライブラリを使えと言われることが仕事でよくあるのだが、非ジェネリクスのIEnumerableやICollectionすら実装されていないコレクションクラスがほとんどなのですごく困る。私はライブラリを名乗るのであれば、こういう基礎的なインタフェースを実装するのは普通のことだと思うのだけど、なんで実装していないのだろうか?ぜひともご意見を拝聴させていただきたいところだが、メールを出すのも面倒だし、そもそも好きでGrapecity社のライブラリを使っているわけではないので、なかなか気が進まない。

そこで、少々危険ではあるが、以下の様な方法も考えてみた。

public static IEnumerable<T> AsEnumerable<T>(dynamic collection)
{
   for (int i = 0; i < collection.Count; i++)
       yield return (T)collection[i];
}

IEnumerableもICollectionも実装していない「なんちゃってコレクション」クラスでも、だいたい、Countプロパティと添字によるインデクサをサポートしている。だから、だいたいのクラスで上記の拡張メソッドは動作する「はず」だ。実行するまでエラーがわからないため、私は怖いので使えないが。

さらに面倒なのは、当然のことであるがdynamicな引数を一つだけ取るような拡張メソッドは定義できない。すると、Extention.AsEnumerable(poorImplementationCollection)みたいな書き方をするしかないので、これも具合が悪い。

ということで、結局はちゃんとオブジェクト指向的なコードを打って、標準ライブラリクラスの意図も尊重しましょうってことになんのかもしれないけど、でも、こういう使いたくもないようなライブラリを使わなくちゃいけない事自体がそもそも問題であるし、仕事って面倒くせえなー。仕事したくねえなあー。と今日も思う。