Effective C# 3rd 読書メモ 36 クエリ式がどのようにメソッド呼び出しにマップされるか理解する

LINQは2つの概念に基づいている: クエリ言語と、クエリ言語から一式のメソッドへの翻訳である。C#コンパイラはクエリ言語で書かれたクエリ式をメソッド呼び出しに変換する。

全てのクエリ式は、1つ以上のメソッド呼び出しにマップされる。このマッピングを理解することは、組み込みのクラスを利用する場合にも、組み込みのクラスを拡張する場合にも重要である。

クエリ式のパターンは11種類ある(詳細は割愛)。

.NETの基本ライブラリは以下の実装を提供している。

  • System.Linq.Enumerableによる、IEnumerable<T>の拡張メソッド(クエリ式の実装)
  • System.Linq.Queryableによる、IQueryable<T>の拡張メソッド(クエリを別形式に変換する)

クエリ式からメソッド呼び出しへの変換はコンパイラによって行われる。

from n in numbersはnumbersの値を1つずつ取り出してnという変数に紐付ける。where節はWhere()メソッドに変換されるフィルターを定義する。

select節はSelect()メソッドに変換されるが、一定条件のもと最適化によって消去されることがある。サンプルのselectは、範囲のある値(range)を、単に別のrangeに変換しているだけである。このようなselectをdegenerate select(退化select)と呼ぶ。degenerate selectは特別な処理を行っていないので、コンパイラはこのselectを消去する。

この結果、上記クエリ式は以下のメソッド呼び出しに変換される。

以下のように、selectが元の値とは異なる値を返す場合には、省略されない。

次に、orderby節を検討する。

ここで、クエリ式は以下のように変換される。

ThenBy()メソッドはOrderBy()ないしThenBy()の結果を受け、追加の並び替えを行う。

残りは、グルーピングと、複数のfrom句の使用による継続(continuations)の実現である。継続を含むクエリ式は、ネストされたクエリに変換され、ネストされたクエリがメソッド呼び出しに変換される。

上記コードは、継続によって以下のように変換される。

ネストされたクエリができたら、メソッド呼び出しに変換される。

最後はSelectMany()Join()、そしてGroupJoin()である。

上記コードでは、16個のペアが作成される。このクエリ式のように、複数のfrom句を含むクエリ式は、SelectMany()メソッドに変換される。

なお、SelectMany()の内部では以下のような処理が行われている。

joinJoin()に変換される。

into節を含むjoin式はGroupJoin()に変換される。

個人的な感想

複数のselectjoinが出てくる場合はクエリ式のほうが簡潔に書けるけど、それ以外の場合はメソッド呼び出しのほうが簡潔に書ける。クエリ式を使う機会はそんなに多くなさそうな気がする。まあ、自分が書かなくても、他の人が書いたものを読む機会はあるので、クエリ式=>メソッド呼び出しの変換規則は、知っておいて損はない。

コメントを残す