クエリ構文とメソッド構文 (LINQ)
更新 : 2007 年 11 月
LINQ の入門ドキュメントに記載されているほとんどのクエリは、C# 3.0 で導入された宣言クエリ構文を使用して、クエリ式として記述されています。ただし、.NET 共通言語ランタイム (CLR: Common Language Runtime) 自体には、クエリ構文の概念はありません。したがって、クエリ式は、コンパイル時に CLR が理解できる形式、つまりメソッド呼び出しに変換されます。これらのメソッドは標準クエリ演算子と呼ばれ、Where、Select、GroupBy、Join、Max、Average などの名前が付いています。クエリ構文ではなくメソッド構文を使用することで、これらのメソッドを直接呼び出すことができます。
通常は、クエリ構文の方が単純で読みやすいため、クエリ構文を使用することをお勧めします。ただし、メソッド構文とクエリ構文には意味上の違いはありません。また、指定した条件に一致する要素の数を取得するクエリや、ソース シーケンス内で最大値を持つ要素を取得するクエリなど、一部のクエリはメソッド呼び出しとしてしか表すことができません。System.Linq 名前空間の標準クエリ演算子のリファレンス ドキュメントでは、通常はメソッド構文を使用しています。このため、LINQ クエリを初めて作成する場合でも、クエリとクエリ式自体でメソッド構文を使用する方法に慣れておくと便利です。
標準クエリ演算子の拡張メソッド
単純なクエリ式と、メソッド ベースのクエリとして作成された意味的に同等のクエリを次の例に示します。
class QueryVMethodSyntax
{
static void Main()
{
int[] numbers = { 5, 10, 8, 3, 6, 12};
//Query syntax:
IEnumerable<int> numQuery1 =
from num in numbers
where num % 2 == 0
orderby num
select num;
//Method syntax:
IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n);
foreach (int i in numQuery1)
{
Console.Write(i + " ");
}
Console.WriteLine(System.Environment.NewLine);
foreach (int i in numQuery2)
{
Console.Write(i + " ");
}
// Keep the console open in debug mode.
Console.WriteLine(System.Environment.NewLine);
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
}
/*
Output:
6 8 10 12
6 8 10 12
*/
2 つの例の出力結果は同じです。どちらの形式でも、クエリ変数の型は同じ IEnumerable<T> であることがわかります。
メソッド ベースのクエリを理解するために、この例を詳しく見てみましょう。式の右側では、where 句が numbers オブジェクトのインスタンス メソッドとして表されています。このオブジェクトを後で呼び出すとき、型は IEnumerable<int> になります。ジェネリック IEnumerable<T> インターフェイスに慣れている方は、このインターフェイスが Where メソッドを持たないことにお気付きでしょう。しかし、Visual Studio IDE で IntelliSense のコンプリート リストを呼び出すと、Where メソッドだけでなく、Select、SelectMany、Join、Orderby などの他の多くのメソッドも表示されます。これらはすべて標準クエリ演算子です。
IEnumerable<T> は、これらの追加メソッドを含むように再定義されたように見えますが、実際にはそうではありません。標準クエリ演算子は、拡張メソッドという新しい種類のメソッドとして実装されます。拡張メソッドは既存の型を "拡張" し、その型のインスタンス メソッドであるかのように呼び出すことができます。numbers.Where(...) という記述ができるのは、標準クエリ演算子によって IEnumerable<T> が拡張されるためです。
初めて LINQ を使用する場合、拡張メソッドに関しては、正しい using ディレクティブを使用してアプリケーションのスコープに取り込む方法のみを理解しておけば十分です。これについては、「方法 : LINQ プロジェクトを作成する」で詳しく説明しています。アプリケーション側から見ると、拡張メソッドと通常のインスタンス メソッドは同じです。
拡張メソッドの詳細については、「拡張メソッド (C# プログラミング ガイド)」を参照してください。標準クエリ演算子の詳細については、「LINQ の一般的なプログラミング ガイド」および「標準クエリ演算子の概要」を参照してください。LINQ to SQL や LINQ to XML などの一部の LINQ プロバイダでは、独自の標準クエリ演算子と、IEnumerable<T> 以外の型に対応する拡張メソッドが実装されています。
ラムダ式
前の例では、Where(num => num % 2 == 0) のように、条件式 (num % 2 == 0) がインライン引数として Where メソッドに渡されています。このインライン式はラムダ式と呼ばれます。これは、匿名メソッド、汎用デリゲート、または式ツリーのような複雑な形式で記述する必要のあるコードを作成するのに便利な方法です。C# では、=> がラムダ演算子で、"~に入力" という意味を持ちます。演算子の左側の num は、クエリ式の num に対応する入力変数です。numbers はジェネリック IEnumerable<T> 型であることがわかっているため、コンパイラは num の型を推論できます。ラムダの本体は、クエリ構文での式や、他の C# 式またはステートメントでの式と同じです。これには、メソッド呼び出しやその他の複雑なロジックを含めることができます。"戻り値" は式の結果です。
LINQ を使い始めるうえで、ラムダを多用する必要はありません。ただし、メソッド構文でしか表せないクエリもあり、その一部ではラムダ式が必要になります。ラムダに慣れると、LINQ ツールボックスの中でも強力かつ柔軟なツールであることがわかります。詳細については、「ラムダ式 (C# プログラミング ガイド)」を参照してください。
クエリの作成のしやすさ
前のコード例では、OrderBy メソッドは Where の呼び出しに対してドット演算子を使用することで呼び出されています。Where はフィルタ処理されたシーケンスを生成し、Orderby はそのシーケンスを操作して並べ替えます。クエリは IEnumerable を返すため、メソッド構文では、メソッド呼び出しを連結することでクエリを作成します。クエリ構文を使用してクエリを作成すると、この処理がコンパイラの内部で行われます。クエリ変数はクエリの結果を格納しないため、クエリを実行した後でも、いつでもクエリを変更したり、新しいクエリの基として使用したりできます。