LINQ でのクエリ構文とメソッド構文 (C#)Query Syntax and Method Syntax in LINQ (C#)

統合言語クエリ (LINQLINQ) の入門的なドキュメントでは、ほとんどのクエリが、LINQ の宣言型クエリ構文を使用して記述されています。Most queries in the introductory Language Integrated Query (LINQLINQ) documentation are written by using the LINQ declarative query syntax. ただし、クエリ構文は、コードのコンパイル時に、.NET 共通言語ランタイム (CLR) 用のメソッド呼び出しに変換する必要があります。However, the query syntax must be translated into method calls for the .NET common language runtime (CLR) when the code is compiled. これらのメソッド呼び出しが、標準クエリ演算子 (WhereSelectGroupByJoinMaxAverage など) を呼び出します。These method calls invoke the standard query operators, which have names such as Where, Select, GroupBy, Join, Max, and Average. これらは、クエリ構文ではなくメソッド構文を使用して直接呼び出すことができます。You can call them directly by using method syntax instead of query syntax.

クエリ構文とメソッド構文は意味的には同じものですが、多くの人は、クエリ構文のほうがシンプルで読みやすいと感じます。Query syntax and method syntax are semantically identical, but many people find query syntax simpler and easier to read. 一部のクエリは、メソッド呼び出しとして表現する必要があります。Some queries must be expressed as method calls. たとえば、指定した条件に一致する要素の数を取得するクエリを表すには、メソッド呼び出しを使用する必要があります。For example, you must use a method call to express a query that retrieves the number of elements that match a specified condition. また、ソース シーケンスで最大の値を持つ要素を取得するクエリにも、メソッド呼び出しを使用する必要があります。You also must use a method call for a query that retrieves the element that has the maximum value in a source sequence. System.Linq 名前空間の標準クエリ演算子のリファレンス ドキュメントでは、通常、メソッド構文が使用されます。The reference documentation for the standard query operators in the System.Linq namespace generally uses method syntax. そのため、LINQLINQ クエリの記述をこれから学習する初心者にとっても、クエリやクエリ式自体の中でメソッド構文をどのように使用すればよいか理解しておくことは有用です。Therefore, even when getting started writing LINQLINQ queries, it is useful to be familiar with how to use method syntax in queries and in query expressions themselves.

標準クエリ演算子の拡張メソッドStandard Query Operator Extension Methods

次の例は、シンプルなクエリ式と、メソッド ベースのクエリとして記述された、意味的に同等のクエリを示したものです。The following example shows a simple query expression and the semantically equivalent query written as a method-based query.

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 + " ");
        foreach (int i in numQuery2)
            Console.Write(i + " ");
        // Keep the console open in debug mode.
        Console.WriteLine("Press any key to exit");
    6 8 10 12
    6 8 10 12

2 つの例からの出力は同じです。The output from the two examples is identical. クエリ変数の型は、どちらの形式でも同じです: IEnumerable<T>You can see that the type of the query variable is the same in both forms: IEnumerable<T>.

メソッド ベースのクエリを理解するために、より詳しく調べていきましょう。To understand the method-based query, let's examine it more closely. 式の右側を見ると、where 句が numbers オブジェクトのインスタンス メソッドとして表されていることがわかります。これは、既におわかりのように、IEnumerable<int> の型を持っています。On the right side of the expression, notice that the where clause is now expressed as an instance method on the numbers object, which as you will recall has a type of IEnumerable<int>. ジェネリック型の IEnumerable<T> インターフェイスについて知識があれば、これが Where メソッドではないことがわかるでしょう。If you are familiar with the generic IEnumerable<T> interface, you know that it does not have a Where method. しかし、Visual Studio IDE で IntelliSense の入力補完リストを呼び出すと、Where メソッドだけでなく、SelectSelectManyJoinOrderby など、他にも多くのメソッドが表示されます。However, if you invoke the IntelliSense completion list in the Visual Studio IDE, you will see not only a Where method, but many other methods such as Select, SelectMany, Join, and Orderby. これらはすべて、標準クエリ演算子です。These are all the standard query operators.

Intellisense の標準クエリ演算子をすべて示すスクリーンショット。

一見、IEnumerable<T> が再定義され、これらのメソッドが追加されたかのように見えますが、実際にはそうではありません。Although it looks as if IEnumerable<T> has been redefined to include these additional methods, in fact this is not the case. 標準クエリ演算子は、拡張メソッドという新しい種類のメソッドとして実装されています。The standard query operators are implemented as a new kind of method called extension methods. 拡張メソッドは、既存の型を "拡張" します。これらは、あたかもその型のインスタンス メソッドであるかのように呼び出すことができます。Extensions methods "extend" an existing type; they can be called as if they were instance methods on the type. 標準クエリ演算子が IEnumerable<T> を拡張しているため、numbers.Where(...) を書き込むことができます。The standard query operators extend IEnumerable<T> and that is why you can write numbers.Where(...).

LINQLINQ の初心者が拡張メソッドについて知っておくべき最も重要なことは、適切な using ディレクティブを使用して、アプリケーションのスコープ内にそれらを取り込む方法です。To get started using LINQLINQ, all that you really have to know about extension methods is how to bring them into scope in your application by using the correct using directives. アプリケーションの観点から見れば、拡張メソッドは通常のインスタンス メソッドと同じものです。From your application's point of view, an extension method and a regular instance method are the same.

拡張メソッドについて詳しくは、「拡張メソッド」をご覧ください。For more information about extension methods, see Extension Methods. 標準クエリ演算子について詳しくは、「標準クエリ演算子の概要 (C#)」をご覧ください。For more information about standard query operators, see Standard Query Operators Overview (C#). 一部の LINQLINQ プロバイダー (LINQ to SQLLINQ to SQLLINQ to XMLLINQ to XML など) では、IEnumerable<T> 以外の型に対応するため、独自の標準クエリ演算子と追加の拡張メソッドを実装しています。Some LINQLINQ providers, such as LINQ to SQLLINQ to SQL and LINQ to XMLLINQ to XML, implement their own standard query operators and additional extension methods for other types besides IEnumerable<T>.

ラムダ式Lambda Expressions

上記の例では、条件式 (num % 2 == 0) がインライン引数として Where メソッドに渡されています:Where(num => num % 2 == 0). このインライン式は、ラムダ式と呼ばれます。In the previous example, notice that the conditional expression (num % 2 == 0) is passed as an in-line argument to the Where method: Where(num => num % 2 == 0). This inline expression is called a lambda expression. これを使用すると、本来であれば、匿名メソッド、ジェネリック デリゲート、式ツリーなど、より複雑な形式で記述しなければならないコードを、簡単に記述できます。It is a convenient way to write code that would otherwise have to be written in more cumbersome form as an anonymous method or a generic delegate or an expression tree. C# では、=> がラムダ演算子で、"goes to" という読み方をします。In C# => is the lambda operator, which is read as "goes to". 演算子の左側にある num は、クエリ式の num に対応する入力変数です。The num on the left of the operator is the input variable which corresponds to num in the query expression. コンパイラは、numbers がジェネリック IEnumerable<T> 型であることがわかっているため、num の型を推論できます。The compiler can infer the type of num because it knows that numbers is a generic IEnumerable<T> type. ラムダの本体は、クエリ構文や、C# のその他の式やステートメントの式と同じです。これには、メソッド呼び出しやその他の複雑なロジックを含めることができます。The body of the lambda is just the same as the expression in query syntax or in any other C# expression or statement; it can include method calls and other complex logic. "戻り値" は、式の結果だけです。The "return value" is just the expression result.

LINQLINQ の初心者の場合、ラムダを広範に使用する必要はありません。To get started using LINQLINQ, you do not have to use lambdas extensively. ただし、一部のクエリはメソッド構文でしか表現できず、ラムダ式が必須となるものもあります。However, certain queries can only be expressed in method syntax and some of those require lambda expressions. ラムダに慣れてきたら、これが LINQLINQ のツールボックスで使用できる強力で柔軟なツールであることがおわかりいただけるでしょう。After you become more familiar with lambdas, you will find that they are a powerful and flexible tool in your LINQLINQ toolbox. 詳細については、「ラムダ式」を参照してください。For more information, see Lambda Expressions.

クエリの構成可能性Composability of Queries

上記の例で、OrderBy メソッドは Where への呼び出しでドット演算子を使用して起動されています。In the previous code example, note that the OrderBy method is invoked by using the dot operator on the call to Where. Where は、フィルター処理されたシーケンスを生成し、その後 Orderby は、そのシーケンスをソートして操作しています。Where produces a filtered sequence, and then Orderby operates on that sequence by sorting it. クエリが IEnumerable を返すので、開発者は、メソッド呼び出しをつないでいきながら、メソッド構文でそれらを編成します。Because queries return an IEnumerable, you compose them in method syntax by chaining the method calls together. これが、クエリ構文を使ってクエリを記述する際に、コンパイラがバック グラウンドで行っていることなのです。This is what the compiler does behind the scenes when you write queries by using query syntax. また、クエリ変数にはクエリの結果が格納されないので、開発者はそれが実行された後でも、それを随時変更したり、新しいクエリのベースとして使用することができます。And because a query variable does not store the results of the query, you can modify it or use it as the basis for a new query at any time, even after it has been executed.