LINQという名のIEnumerable革命

テストのためにVisual C# 2008 Express Editionを入れてみた。
これは.NET Framework 3.5に対応したプログラムが書けるのでいろいろやってみた。
まずデリゲートの簡略化というのが1つある。ラムダ式と言うらしい。
デリゲートの型としてFunc<T,TResult>などが導入された。

Func<double, double> v = 
t => { return 100 * Math.Sqrt(2) * Math.Sin(2 * Math.PI * 60 * t); };

数学の関数を定義するかのように簡単にデリゲートが作れると言うこと。
これはvという名前で引数にdouble1つ、返り値にdoubleを返す関数のデリゲートということ。
引数の名前を1つならt、2つ以上なら(vrms,t)のようにして、=>とやって右にいろいろ書けばいい。
{}でくくって複雑な動作を書いてもいいし、もっと簡略化して返り値を直接書いてもいい。
Func<double, double> f = t => t*2;という風にね。というかこれがラムダ式の本義ですね。
これはデリゲートの創作意欲が出てくる便利な書き方だ。
いままでも匿名メソッドってのでデリゲートは作れたけど匿名だったからなぁ…おもしろくない。
さて、これを使うことでいろいろおもしろいことが出来ます。
で、LINQというのがあるのだが、僕はこれをIEnumerable革命だと思っている。
IEnumerableは繰り返し可能、すなわちforeachできると取り決めをするインターフェース。
データベースをIEnumerableを実装したクラスのインスタンスで取り扱うというのがLINQの考えらしい。
で、データベースを操作するようにIEnumerableを操作できるという機能をたくさん搭載した。
けど、IEnumerableはインターフェース、インターフェースは動作を決めることができない。
なので抽象クラスみたいに機能が追加できるわけじゃない。
そこで考えられたのが拡張メソッドかもしれない。

System.Linq.Enumerable.Where<classmate>(foos, x => { return x.id % 2 == 0; });
foos.Where(x => { return x.id % 2 == 0; });

本来上で書くべきことを下の方法で簡略化できることなんだが、なんのことだかさっぱり。
どうもよくわからんのだがそういう特別な静的メソッドを定義してあるらしい。それを拡張メソッドと言うようだ。
拡張メソッドはとりあえず自作しないので、これを使うことを考えよう。
どうもLinqのおかげで、IEnumerableを実装したクラスに新しくいろんな機能が備わった。
IEnumerableを実装したクラスでもっともポピュラーなのは配列だな。

classmate[] foos = new classmate[] { 
new classmate(1,"浅田"), new classmate(2,"井上"),new classmate(3,"尾崎"), new classmate(4,"川上")};
var myfoos = foos.Where(x => { return x.id % 2 == 0; }).Select(x => { return x.name; });
//var myfoos = from foo in foos where foo.id % 2 == 0 select foo.name;
foreach (var foo in myfoos){
Console.WriteLine(foo);
}

というわけでこんなソースコードを作ってみました。
まず配列を作りました。classmateというクラスは自作しましたよ。本当にこんな名前のクラスメイトがいるかは知らないけど。
int型のid、string型のnameというプロパティを持ってる。ちなみにgetしかないので読み取り専用。
で、次の書き方が新しい書き方。まず型類推のvarを使っている。
まぁあんまり型類推は使いたくないけどなぁ…けど何なのか直感的じゃないもので。
後に説明するけど、ここのvarにはIEnumerable<string>を入れれば正解。
さて、件の拡張メソッドにはIEnumerableなのを加工する機能がたくさん入ってる。
それでこれはSQL文に似せて書ける。けどなんか僕は直感的じゃないからあえて使わない。
ちなみにそのSQL文に似せて書いた例はコメントアウトしてあるからわかる人は見ればいい。
なので元の拡張メソッドを呼び出して書くことにする。
この拡張メソッドにはSQL文のWHERE文に相当するWhereメソッドやらいろいろ用意してある。
で引数にはFunc<classmate,bool>なやつを用意すればいい。
この引数で与えたデリゲートでもって条件を指定するわけだ。
そんなデリゲートをラムダ式で簡単に自作できる。便利ですな。
今回は理解しやすかったから=>{}で書いたけど、この程度ならx=>x.id%2==0と書いた方がいいか。
このWhereメソッドはIEnumerableなのから条件を満たすものを選別してくれるというメソッド。
IEnumerable<classmate>を実装しているclassmate[]のときの返り値はIEnumerable<classmate>。
これをさらにSelectメソッドで加工。これもSQL文のSELECT文に由来。
引数にはFunc<classmate,TResult>(TResult任意)を渡せばいい。
今回はここから名前を取り出すデリゲートを作成してやりました。
なので、引数に渡されたのはFunc<classmate,string>、これで1つ1つ加工していくと。
もちろんもっと複雑なデリゲートを作成してもいい。名前の文字の順番を反転させるとか。
x => { char[] cs = x.name.ToCharArray(); Array.Reverse(cs); return new String(cs); }
にすればできるよ。
で、これで加工されて、返り値はIEnumerable<TResult>、今回はIEnumerable<string>ですね。
あとはこれをforeachでループに回した。
これを配列に変換することもできる。myfoos.ToArray();の要領でやればいい。
これも拡張メソッドのおかげ。
というわけでなかなかおもしろい機能が追加されたなぁと言う印象。
これはデーターベースと組み合わせてこそ便利なんだろうけど、特にそんな計画はなし。
だけど配列やコレクションの整理という意味では結構便利だなぁと思った。
まぁけど、.NET Framework 2.0では使えないのが残念。.NET Framework 3.5はいつになれば普及するのだろうか。