次の方法で共有


C での LINQ クエリの概要#

クエリは、データ ソースからデータを取得する式です。 リレーショナル データベース用の SQL や XML 用の XQuery など、異なるデータ ソースには異なるネイティブ クエリ言語があります。 開発者は、サポートする必要があるデータ ソースまたはデータ形式の種類ごとに新しいクエリ言語を学習する必要があります。 LINQ は、データ ソースと形式の種類に対して一貫した C# 言語モデルを提供することで、この状況を簡略化します。 LINQ クエリでは、常に C# オブジェクトを操作します。 LINQ プロバイダーが使用可能な場合は、同じ基本的なコーディング パターンを使用して、XML ドキュメント、SQL データベース、.NET コレクション、およびその他の形式のデータのクエリと変換を行います。

クエリ操作の 3 つの部分

すべての LINQ クエリ操作は、次の 3 つの異なるアクションで構成されます。

  1. データ ソースを取得します。
  2. クエリを作成します。
  3. クエリを実行します。

次の例は、クエリ操作の 3 つの部分をソース コードで表現する方法を示しています。 この例では、便宜上、データ ソースとして整数配列を使用しています。ただし、他のデータ ソースにも同じ概念が適用されます。 この例は、この記事の残りの部分で参照されています。

// The Three Parts of a LINQ Query:
// 1. Data source.
int[] numbers = [ 0, 1, 2, 3, 4, 5, 6 ];

// 2. Query creation.
// numQuery is an IEnumerable<int>
var numQuery = from num in numbers
               where (num % 2) == 0
               select num;

// 3. Query execution.
foreach (int num in numQuery)
{
    Console.Write("{0,1} ", num);
}

次の図は、クエリ操作全体を示しています。 LINQ では、クエリの実行はクエリ自体とは異なります。 つまり、クエリ変数を作成してもデータは取得されません。

LINQ クエリ操作全体の図。

データ ソース

前の例のデータ ソースは、ジェネリック IEnumerable<T> インターフェイスをサポートする配列です。 これは、LINQ を使用してクエリを実行できることを意味します。 クエリは foreach ステートメントで実行され、 foreach には IEnumerable または IEnumerable<T>が必要です。 IEnumerable<T>またはジェネリック IQueryable<T>などの派生インターフェイスをサポートする型は、クエリ可能な型と呼ばれます。

クエリ可能な型では、LINQ データ ソースとして機能するために変更や特別な処理は必要ありません。 ソース データがまだクエリ可能な型としてメモリ内にない場合、LINQ プロバイダーはそれをそのように表す必要があります。 たとえば、LINQ to XML は、クエリ可能な XElement 型に XML ドキュメントを読み込みます。

// Create a data source from an XML document.
// using System.Xml.Linq;
XElement contacts = XElement.Load(@"c:\myContactList.xml");

EntityFramework では、C# クラスとデータベース スキーマの間にオブジェクト リレーショナル マッピングを作成します。 オブジェクトに対してクエリを記述すると、実行時に EntityFramework がデータベースとの通信を処理します。 次の例では、 Customers はデータベース内の特定のテーブルを表し、クエリ結果の型 ( IQueryable<T>) は IEnumerable<T>から派生しています。

Northwnd db = new Northwnd(@"c:\northwnd.mdf");

// Query for customers in London.
IQueryable<Customer> custQuery =
    from cust in db.Customers
    where cust.City == "London"
    select cust;

特定の種類のデータ ソースを作成する方法の詳細については、さまざまな LINQ プロバイダーのドキュメントを参照してください。 ただし、基本的なルールは単純です。LINQ データ ソースは、ジェネリック IEnumerable<T> インターフェイスをサポートする任意のオブジェクト、またはそれを継承するインターフェイス (通常は IQueryable<T>)。

非ジェネリック ArrayList インターフェイスをサポートするIEnumerableなどの型は、LINQ データ ソースとしても使用できます。 詳細については、「 LINQ を使用して ArrayList にクエリを実行する方法 (C#)」を参照してください。

クエリ

このクエリでは、データ ソースまたはソースから取得する情報を指定します。 必要に応じて、クエリでは、返される前にその情報を並べ替え、グループ化、および整形する方法も指定します。 クエリはクエリ変数に格納され、クエリ式で初期化されます。 C# クエリ構文を使用してクエリを記述します。

前の例のクエリは、整数配列からすべての偶数を返します。 クエリ式には、 fromwhereselectの 3 つの句が含まれています。 (SQL に慣れている場合は、句の順序が SQL の順序と逆になっていることに気付きました)。 from 句はデータ ソースを指定し、 where 句はフィルターを適用し、 select 句は返される要素の型を指定します。 このセクションでは、すべてのクエリ句について詳しく説明します。 現時点では、重要な点は、LINQ では、クエリ変数自体がアクションを実行せず、データを返さないということです。 クエリが後で実行されたときに結果を生成するために必要な情報が格納されます。 クエリの作成方法の詳細については、「 標準クエリ演算子の概要 (C#)」を参照してください。

クエリは、メソッド構文を使用して表現することもできます。 詳細については、「LINQ の クエリ構文とメソッド構文」を参照してください。

実行方法による標準クエリ演算子の分類

標準クエリ演算子メソッドの LINQ to Objects 実装は、 即時 または 遅延の 2 つの主な方法のいずれかで実行されます。 遅延実行を使用するクエリ演算子は、さらに、 ストリーミング非ストリーミングの 2 つのカテゴリに分けることができます。

直ちに

即時実行とは、データ ソースが読み取られ、操作が 1 回実行されることを意味します。 スカラー結果を返す標準クエリ演算子はすべて直ちに実行されます。 このようなクエリの例としては、 CountMaxAverageFirstがあります。 クエリ自体が結果を返すためにforeachを使用する必要があるため、これらのメソッドは明示的なforeach ステートメントなしで実行されます。 これらのクエリは、 IEnumerable コレクションではなく、1 つの値を返します。 メソッドまたはEnumerable.ToListメソッドを使用して、Enumerable.ToArrayをすぐに実行するように強制できます。 即時実行では、クエリ宣言ではなくクエリ結果を再利用できます。 結果は 1 回取得され、後で使用するために格納されます。 次のクエリは、ソース配列内の偶数の数を返します。

var evenNumQuery = from num in numbers
                   where (num % 2) == 0
                   select num;

int evenNumCount = evenNumQuery.Count();

クエリの即時実行を強制し、その結果をキャッシュするには、 ToList または ToArray メソッドを呼び出します。

List<int> numQuery2 = (from num in numbers
                       where (num % 2) == 0
                       select num).ToList();

// or like this:
// numQuery3 is still an int[]

var numQuery3 = (from num in numbers
                 where (num % 2) == 0
                 select num).ToArray();

クエリ式の直後に foreach ループを配置して、強制的に実行することもできます。 ただし、 ToList または ToArray を呼び出すことによって、すべてのデータを単一のコレクション オブジェクトにキャッシュすることもできます。

保留中

遅延実行とは、クエリが宣言されているコード内のポイントで操作が実行されていないことを意味します。 この操作は、クエリ変数が列挙されている場合 (たとえば、 foreach ステートメントを使用する場合) にのみ実行されます。 クエリの実行結果は、クエリの定義時ではなく、クエリの実行時のデータ ソースの内容によって異なります。 クエリ変数を複数回列挙すると、毎回結果が異なる場合があります。 戻り値の型が IEnumerable<T> または IOrderedEnumerable<TElement> 遅延方式で実行される、ほぼすべての標準クエリ演算子。 遅延実行では、クエリの結果が反復処理されるたびに、更新されたデータがデータ ソースからフェッチされるため、クエリの再利用機能が提供されます。 次のコードは、遅延実行の例を示しています。

foreach (int num in numQuery)
{
    Console.Write("{0,1} ", num);
}

foreach ステートメントは、クエリ結果を取得する場所でもあります。 たとえば、前のクエリでは、 num 反復変数は、返されたシーケンス内の各値 (一度に 1 つずつ) を保持します。

クエリ変数自体はクエリ結果を保持しないため、繰り返し実行して更新されたデータを取得できます。 たとえば、別のアプリケーションによってデータベースが継続的に更新される場合があります。 アプリケーションでは、最新のデータを取得するクエリを 1 つ作成し、間隔を置いて実行して更新された結果を取得できます。

遅延実行を使用するクエリ演算子は、ストリーミングまたは非ストリーミングとしてさらに分類できます。

ストリーミング

ストリーミング演算子は、要素を生成する前にすべてのソース データを読み取る必要はありません。 実行時に、ストリーミング演算子は、読み取り時に各ソース要素に対して操作を実行し、必要に応じて要素を生成します。 ストリーミング演算子は、結果要素を生成できるようになるまでソース要素の読み取りを続行します。 つまり、1 つの結果要素を生成するために、複数のソース要素が読み取られる可能性があります。

非ストリーミング

非ストリーミング演算子は、結果要素を生成する前にすべてのソース データを読み取る必要があります。 並べ替えやグループ化などの操作は、このカテゴリに分類されます。 実行時に、非ストリーミング クエリ演算子はすべてのソース データを読み取り、データ構造に格納し、操作を実行して、結果の要素を生成します。

分類テーブル

次の表は、各標準クエリ演算子メソッドを、その実行方法に従って分類します。

1 つの演算子が 2 つの列にマークされている場合、2 つの入力シーケンスが操作に関係し、各シーケンスは異なる方法で評価されます。 このような場合、遅延ストリーミング方式で評価されるのは、パラメーター リストの最初のシーケンスです。

標準クエリ演算子 戻り値の型 即時実行 遅延実行 (ストリーミング) 遅延実行 (非ストリーミング)
Aggregate TSource
All Boolean
Any Boolean
AsEnumerable IEnumerable<T>
Average 単一の数値
Cast IEnumerable<T>
Concat IEnumerable<T>
Contains Boolean
Count Int32
DefaultIfEmpty IEnumerable<T>
Distinct IEnumerable<T>
ElementAt TSource
ElementAtOrDefault TSource?
Empty IEnumerable<T>
Except IEnumerable<T>
First TSource
FirstOrDefault TSource?
GroupBy IEnumerable<T>
GroupJoin IEnumerable<T>
Intersect IEnumerable<T>
Join IEnumerable<T>
Last TSource
LastOrDefault TSource?
LongCount Int64
Max 単一の数値、 TSource、または TResult?
Min 単一の数値、 TSource、または TResult?
OfType IEnumerable<T>
OrderBy IOrderedEnumerable<TElement>
OrderByDescending IOrderedEnumerable<TElement>
Range IEnumerable<T>
Repeat IEnumerable<T>
Reverse IEnumerable<T>
Select IEnumerable<T>
SelectMany IEnumerable<T>
SequenceEqual Boolean
Single TSource
SingleOrDefault TSource?
Skip IEnumerable<T>
SkipWhile IEnumerable<T>
Sum 単一の数値
Take IEnumerable<T>
TakeWhile IEnumerable<T>
ThenBy IOrderedEnumerable<TElement>
ThenByDescending IOrderedEnumerable<TElement>
ToArray TSource[] 配列
ToDictionary Dictionary<TKey,TValue>
ToList IList<T>
ToLookup ILookup<TKey,TElement>
Union IEnumerable<T>
Where IEnumerable<T>

LINQ to Objects

"LINQ to Objects" は、任意の IEnumerable または IEnumerable<T> コレクションで直接 LINQ クエリを使用することを指します。 LINQ を使用すると、 List<T>ArrayDictionary<TKey,TValue>など、列挙可能なコレクションに対してクエリを実行できます。 コレクションには、ユーザー定義または .NET API によって返される型を指定できます。 LINQ アプローチでは、取得する内容を記述する宣言型コードを記述します。 LINQ to Objects は、LINQ を使用したプログラミングの優れた概要を提供します。

LINQ クエリには、従来の foreach ループよりも 3 つの主な利点があります。

  • これらは、特に複数の条件をフィルター処理する場合に、より簡潔で読みやすいです。
  • これらは、最小限のアプリケーション コードで強力なフィルター処理、順序付け、およびグループ化の機能を提供します。
  • これらは、ほとんどまたはまったく変更なしで、他のデータ ソースに移植できます。

データに対して実行する操作が複雑になるほど、従来の反復手法ではなく LINQ を使用して実現できるメリットが増えます。

クエリの結果をメモリに格納する

クエリは基本的に、データを取得して整理する方法の一連の手順です。 クエリは、結果内の後続の各項目が要求されると、遅延実行されます。 foreachを使用して結果を反復処理すると、項目はアクセス済みとして返されます。 クエリを評価し、 foreach ループを実行せずに結果を格納するには、クエリ変数に対して次のいずれかのメソッドを呼び出します。

次の例に示すように、クエリ結果を格納するときに、返されたコレクション オブジェクトを新しい変数に割り当てる必要があります。

List<int> numbers = [ 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 ];

IEnumerable<int> queryFactorsOfFour = from num in numbers
                                      where num % 4 == 0
                                      select num;

// Store the results in a new variable
// without executing a foreach loop.
var factorsofFourList = queryFactorsOfFour.ToList();

// Read and write from the newly created list to demonstrate that it holds data.
Console.WriteLine(factorsofFourList[2]);
factorsofFourList[2] = 0;
Console.WriteLine(factorsofFourList[2]);

こちらも参照ください