LINQ でのクエリ構文とメソッド構文 (C#)
入門統合言語クエリ (LINQ) ドキュメントのほとんどのクエリでは、LINQ の宣言クエリ構文を使用して書き込まれます。ただし、クエリの構文は、.NET 共通言語ランタイム (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 to SQL や LINQ to XML などの一部の LINQ プロバイダーは、独自の標準クエリ演算子と、IEnumerable<T> 以外の型に対応する追加の拡張メソッドを実装しています。
ラムダ式
前の例では、条件式 (num % 2 == 0) が Where のインライン メソッドに引数として渡すことに注意してください: Where(num => num % 2 == 0).このインライン式はラムダ式と呼ばれます。これは、匿名メソッド、汎用デリゲート、または式ツリーのような複雑な形式で記述する必要のあるコードを作成するのに便利な方法です。C# では、=> がラムダ演算子で、"~に入力" という意味を持ちます。演算子の左側の num は、クエリ式の num に対応する入力変数です。numbers はジェネリック IEnumerable<T> 型であることがわかっているため、コンパイラは num の型を推論できます。ラムダの本体は、クエリ構文での式や、他の C# 式またはステートメントでの式と同じです。これには、メソッド呼び出しやその他の複雑なロジックを含めることができます。"戻り値" は式の結果です。
LINQ を使い始めるうえで、ラムダを多用する必要はありません。ただし、メソッド構文でしか表せないクエリもあり、その一部ではラムダ式が必要になります。ラムダに慣れると、LINQ ツールボックスの中でも強力かつ柔軟なツールであることがわかります。詳細については、「ラムダ式 (C# プログラミング ガイド)」を参照してください。
クエリの作成のしやすさ
前のコード例では、OrderBy メソッドは Where の呼び出しに対してドット演算子を使用することで呼び出されています。Where はフィルター処理されたシーケンスを生成し、Orderby はそのシーケンスを操作して並べ替えます。クエリは IEnumerable を返すため、メソッド構文では、メソッド呼び出しを連結することでクエリを作成します。クエリ構文を使用してクエリを作成すると、この処理がコンパイラの内部で行われます。クエリ変数はクエリの結果を格納しないため、クエリを実行した後でも、いつでもクエリを変更したり、新しいクエリの基として使用したりできます。