C#3.0とLINQ

LtUのこの記事から。

LINQは.NETのクラスに特定のメソッドを用意させることで、

var q =
	from c in Customers
	where c.City == "London"
	select c;

という式を埋め込め実行できるようにするもの(実際はCustomersのWhere、Selectメソッドを呼ぶ)。ほかにvar変数や、lambda形式や、無名構造体などがselect式を書きやすくするために導入されている。

あとはDLinq、XLinqと用意していて、RDBXMLとも連携しやすくなっているようです。仕様よりも101Samplesをみるのがわかりやすいかも。

内部的にどうなる?

理解のため、仕様書にしたがって上式をメソッド呼び出し表現にすると、

var q = this.Customers.Where(c => c.City == "London").Select(c => c);

となる。さらに推論している型を全部明示的に埋めてみると、

IEnumerable<Customer> q = this.Customers.Where<Customer>(delegate (Customer c) {return c.City == "London";}).Select<Customer, Customer>(delegate (Customer c) { return c;});

となる。そしてさらに、SequenceクラスによるIEnumerableへのstatic importをstatic呼び出しにしたら

IEnumerable<Customer> q = Sequence.Select<Customer, Customer>(Sequence.Where<Customer>(this.Customers, delegate (Customer c) {return c.City == "London";}), delegate (Customer c) { return c;});

となります。C#2.0互換の場合はこのようになるでしょうか。メソッドの型パラメータはすべて推論できそうなのでC#2.0なら以下のでよいかも。

IEnumerable<Customer> q = Sequence.Select(Sequence.Where(this.Customers, delegate (Customer c) {return c.City == "London";}), delegate (Customer c) { return c;});

ちなみにSelectやWhereメソッドは定義上は、

delegate S Func<T, S>(T arg);
class Selector {
  public static IEnumerable<T> Where<T>(this IEnumerable<T> source, Func<T, bool> predicate) {
    foreach (T elem in source) {
      if (predicate(elem)) yield return elem;
    }
  }
  public static IEnumerable<S> Select<T, S>(this IEnumerable<T> source, Func<T, S> selector) {
    foreach (T elem in source) {
      yield return selector(elem);
    }
  }
  ...
}

らしいです。

Comprehension

このLINQのselectはComprehensionに似ていて、Pythonだと上記は

q = [c for c in Customers if c.City == "London"]
#    ~ - select
#          ~~~~~~~~~~~~~~ - from
#                            ~~~~~~~~~~~~~~~~~~ - where

というのと同じような感じでしょうか。IEnumerableなのでlistではなく、generatorのcomprehensionのほうがあってるかも。

q = (c for c in Customers if c.City == "London")

C#3.0=C#2.0+LINQ?

LINQのページ見ると、下にC#3.0の仕様があるんですね。C#3.0はC#2.0にこのLINQを統合したもののようです。というかC#3.0での拡張全体=LINQということかな?C#3.0はComega統合があるのかと思ってたから、いまいち残念。asyncだけでもほしかった。