標準クエリ演算子の概要 (C#)

"標準クエリ演算子" は、LINQ パターンを形成するメソッドです。 これらのメソッドの大部分はシーケンスに対して機能します。ここでシーケンスとは、IEnumerable<T> インターフェイスまたは IQueryable<T> インターフェイスを実装している型のオブジェクトのことです。 標準クエリ演算子には、フィルター処理、プロジェクション、集計、並べ替えなどのクエリ機能が用意されています。

LINQ 標準クエリ演算子には 2 つのセットがあります。1 つは IEnumerable<T> 型のオブジェクトを操作する演算子、もう 1 つは IQueryable<T> 型のオブジェクトを操作する演算子です。 各セットを構成するメソッドは、それぞれ、Enumerable および Queryable クラスの静的メンバーです。 そのメソッドの操作対象である型の "拡張メソッド" として定義されています。 拡張メソッドは、静的メソッド構文またはインスタンス メソッド構文のいずれかを使用して呼び出すことができます。

さらに、いくつかの標準クエリ演算子メソッドが、IEnumerable<T> または IQueryable<T> を基にする型以外の型を操作します。 Enumerable 型は、このような 2 つのメソッドを定義し、その両方が IEnumerable 型のオブジェクトを操作します。 これらのメソッド Cast<TResult>(IEnumerable)OfType<TResult>(IEnumerable) を使用して、LINQ パターンでクエリされるパラメーター化されていないまたは非ジェネリック型のコレクションを有効にすることができます。 これを行うには、厳密に型指定されたオブジェクトのコレクションを作成します。 Queryable クラスは、型 IQueryable のオブジェクトを操作する 2 つの類似したメソッド Cast<TResult>(IQueryable)OfType<TResult>(IQueryable) を定義します。

標準クエリ演算子の実行のタイミングは、シングルトン値を返すか、値のシーケンスを返すかで異なります。 これらのシングルトン値を返すメソッド (たとえば、AverageSum) は、すぐに実行されます。 シーケンスを返すメソッドは、クエリの実行を遅延させ、列挙可能なオブジェクトを返します。

メモリ内コレクションを操作するメソッド、つまり IEnumerable<T> を拡張するメソッドの場合、返される列挙可能なオブジェクトは、メソッドに渡された引数をキャプチャします。 オブジェクトが列挙されると、クエリ演算子のロジックが使用され、クエリ結果が返されます。

対照的に、IQueryable<T> を拡張するメソッドではいかなるクエリ動作も実装されません。 実行するクエリを表す式ツリーがビルドされます。 クエリの処理は、ソース IQueryable<T> オブジェクトによって処理されます。

クエリ メソッドの呼び出しは 1 回のクエリにまとめてチェーン化できるため、クエリが複雑になることがあります。

次のコード例は、標準クエリ演算子を使用してシーケンスに関する情報を取得する方法を示しています。

string sentence = "the quick brown fox jumps over the lazy dog";  
// Split the string into individual words to create a collection.  
string[] words = sentence.Split(' ');  
  
// Using query expression syntax.  
var query = from word in words  
            group word.ToUpper() by word.Length into gr  
            orderby gr.Key  
            select new { Length = gr.Key, Words = gr };  
  
// Using method-based query syntax.  
var query2 = words.  
    GroupBy(w => w.Length, w => w.ToUpper()).  
    Select(g => new { Length = g.Key, Words = g }).  
    OrderBy(o => o.Length);  
  
foreach (var obj in query)  
{  
    Console.WriteLine("Words of length {0}:", obj.Length);  
    foreach (string word in obj.Words)  
        Console.WriteLine(word);  
}  
  
// This code example produces the following output:  
//  
// Words of length 3:  
// THE  
// FOX  
// THE  
// DOG  
// Words of length 4:  
// OVER  
// LAZY  
// Words of length 5:  
// QUICK  
// BROWN  
// JUMPS

クエリ式の構文

頻繁に使用される標準クエリ演算子の中には、C# および Visual Basic 言語専用のキーワード構文が使用されているものがあります。こうした構文では、標準クエリ演算子を、"クエリ" の一部として呼び出すことができます。 専用キーワードおよびそれに対応する構文が使用されている標準クエリ演算子の詳細については、「標準クエリ演算子のクエリ式構文 (C#)」を参照してください。

標準クエリ演算子の拡張

標準クエリ演算子のセットを拡張するには、対象のドメインまたはテクノロジに適したドメイン固有のメソッドを作成します。 また、標準クエリ演算子を、リモート評価、クエリ変換、最適化などの追加サービスが用意されている独自の実装で置き換えることもできます。 例については、「AsEnumerable」を参照してください。

データ ソースを取得する

LINQ クエリで必要な最初の手順は、データ ソースを指定することです。 ほとんどのプログラミング言語と同じように、C# でも、変数を使用する前に宣言しておく必要があります。 LINQ クエリでは、データ ソース (customers) および "範囲変数" (cust) を導入するために from 句が最初に使用されます。

//queryAllCustomers is an IEnumerable<Customer>
var queryAllCustomers = from cust in customers
                        select cust;

範囲変数は、foreachループの反復変数と似ていますが、クエリ式では実際の反復は発生しません。 クエリが実行されると、範囲変数は customers の連続する各要素への参照として機能します。 cust の型はコンパイラで推論できるため、明示的に指定する必要はありません。 追加の範囲変数は、let 句で導入できます。 詳しくは、「let 句」をご覧ください。

注意

ArrayList などの非ジェネリック データ ソースの場合は、範囲変数を明示的に型指定する必要があります。 詳細については、「LINQ を使用して ArrayList にクエリを実行する方法 (C#)」および「from 句」を参照してください。

フィルター処理

最も一般的なクエリ操作は、ブール式の形式でフィルターを適用することです。 クエリにフィルターを使用すると、式の条件に該当する要素だけがクエリから返されます。 結果は、where 句を使って生成されます。 フィルターは、実質的にはソース シーケンスから除外する要素を指定します。 次の例では、住所がロンドンにある customers だけが返されます。

var queryLondonCustomers = from cust in customers
                           where cust.City == "London"
                           select cust;

使い慣れた C# 論理 AND 演算子と論理 OR 演算子を使用すると、必要なだけのフィルター式を where 句に適用できます。 たとえば、住所が "London" にあり、かつ (AND) 名前が "Devon" の顧客だけを返すには、次のコードを記述します。

where cust.City == "London" && cust.Name == "Devon"

住所がロンドンまたはパリにある顧客を返すには、次のコードを記述します。

where cust.City == "London" || cust.City == "Paris"

詳しくは、「where 句」をご覧ください。

順序

返されたデータを並べ替えると便利なことがよくあります。 orderby 句を使用すると、並べ替える型の既定の比較子に従って、返されたシーケンスの要素が並べ替えられます。 たとえば、次のクエリは Name プロパティに基づいて結果を並び替えるように拡張できます。 Name は文字列であるため、既定の比較子によって、アルファベット順 (A から Z) で並べ替えられます。

var queryLondonCustomers3 =
    from cust in customers
    where cust.City == "London"
    orderby cust.Name ascending
    select cust;

結果を逆の順序 (Z から A) で並び替えるには、orderby…descending 句を使用します。

詳細については、「orderby 句」を参照してください。

グループ化

指定したキーに基づいて結果をグループ化するには、group 句を使用します。 たとえば、結果を City 別にグループ化するように指定して、住所がロンドンまたはパリにあるすべての顧客を個々のグループに分けることができます。 この場合は、cust.City がキーになります。

// queryCustomersByCity is an IEnumerable<IGrouping<string, Customer>>
  var queryCustomersByCity =
      from cust in customers
      group cust by cust.City;

  // customerGroup is an IGrouping<string, Customer>
  foreach (var customerGroup in queryCustomersByCity)
  {
      Console.WriteLine(customerGroup.Key);
      foreach (Customer customer in customerGroup)
      {
          Console.WriteLine("    {0}", customer.Name);
      }
  }

group 句を使用したクエリが終了すると、結果は複数リストのリストという形式になります。 リストの各要素はオブジェクトであり、そのオブジェクトには、Key メンバーとそのキーに基づいてグループ化された要素のリストが含まれます。 グループのシーケンスを生成するクエリを反復処理する場合は、入れ子になった foreach ループを使用する必要があります。 外側のループは各グループを反復処理し、内側のループは各グループのメンバーを反復処理します。

グループ操作の結果を参照する必要がある場合は、into キーワードを使用して、さらに照会可能な識別子を作成します。 次のクエリでは、顧客が 2 人より多いグループだけが返されます。

// custQuery is an IEnumerable<IGrouping<string, Customer>>
var custQuery =
    from cust in customers
    group cust by cust.City into custGroup
    where custGroup.Count() > 2
    orderby custGroup.Key
    select custGroup;

詳しくは、「group 句」をご覧ください。

結合

結合操作は、データ ソースで明示的にモデル化されていないシーケンス間に関連付けを作成します。 たとえば、結合を実行して、住所地が同じすべての顧客と販売業者を検索することができます。 LINQ では、join 句はデータベース テーブルを直接の対象とするのではなく、オブジェクトのコレクションを対象として機能します。

var innerJoinQuery =
    from cust in customers
    join dist in distributors on cust.City equals dist.City
    select new { CustomerName = cust.Name, DistributorName = dist.Name };

LINQ では、SQL ほど頻繁に join を使用する必要はありません。これは、オブジェクト モデルでは、LINQ の外部キーが項目のコレクションを保持するプロパティとして表されるためです。 たとえば、Customer オブジェクトには Order オブジェクトのコレクションが含まれます。 結合を実行しなくても、ドット表記を使用して注文にアクセスできます。

from order in Customer.Orders...  

詳細については、「join 句」を参照してください。

選択 (投影)

select 句はクエリの結果を生成し、返される各要素の "シェイプ" つまり型を指定します。 たとえば、完全な Customer オブジェクト、1 つのメンバーのみ、メンバーのサブセット、または計算や新しいオブジェクトの作成に基づいた、まったく異なる種類の結果のいずれで結果が構成されるかを指定できます。 select 句でソース要素のコピー以外のものを生成する場合、その操作は投影と呼ばれます。 投影を使用したデータの変換は、LINQ クエリ式の強力な機能です。 詳細については、「LINQ によるデータ変換 (C#)」と「select 句」を参照してください。

クエリ式の構文表

次の表は、同等なクエリ式の句がある標準クエリ演算子の一覧です。

メソッド C# のクエリ式の構文
Cast 明示的に型指定された範囲変数を使用します。例:

from int i in numbers

(詳しくは、「from 句」をご覧ください。)
GroupBy group … by

\- または -

group … by … into …

(詳しくは、「group 句」をご覧ください。)
GroupJoin<TOuter,TInner,TKey,TResult>(IEnumerable<TOuter>, IEnumerable<TInner>, Func<TOuter,TKey>, Func<TInner,TKey>, Func<TOuter,IEnumerable<TInner>, TResult>) join … in … on … equals … into …

(詳しくは、「join 句」をご覧ください。)
Join<TOuter,TInner,TKey,TResult>(IEnumerable<TOuter>, IEnumerable<TInner>, Func<TOuter,TKey>, Func<TInner,TKey>, Func<TOuter,TInner,TResult>) join … in … on … equals …

(詳しくは、「join 句」をご覧ください。)
OrderBy<TSource,TKey>(IEnumerable<TSource>, Func<TSource,TKey>) orderby

(詳しくは、「orderby 句」をご覧ください。)
OrderByDescending<TSource,TKey>(IEnumerable<TSource>, Func<TSource,TKey>) orderby … descending

(詳しくは、「orderby 句」をご覧ください。)
Select select

(詳しくは、「select 句」をご覧ください。)
SelectMany 複数の from 句を使用します。

(詳しくは、「from 句」をご覧ください。)
ThenBy<TSource,TKey>(IOrderedEnumerable<TSource>, Func<TSource,TKey>) orderby …, …

(詳しくは、「orderby 句」をご覧ください。)
ThenByDescending<TSource,TKey>(IOrderedEnumerable<TSource>, Func<TSource,TKey>) orderby …, … descending

(詳しくは、「orderby 句」をご覧ください。)
Where where

(詳しくは、「where 句」をご覧ください。)

関連項目