次の方法で共有


LINQ クエリ操作の型リレーションシップ (C#)

クエリを効果的に記述するには、完全なクエリ操作の変数の型がすべて相互にどのように関連しているかを理解する必要があります。 これらの関係を理解すれば、ドキュメント内の LINQ サンプルとコード例をより簡単に理解できます。 さらに、 varを使用して変数を暗黙的に型指定するとどうなるかを理解できます。

LINQ クエリ操作は、データ ソース、クエリ自体、およびクエリ実行で厳密に型指定されます。 クエリ内の変数の型は、データ ソース内の要素の型と、 foreach ステートメントの反復変数の型と互換性がある必要があります。 この厳密な型指定により、ユーザーがエラーを検出する前に修正できる場合に、コンパイル時に型エラーがキャッチされます。

これらの型リレーションシップを示すために、次の例のほとんどは、すべての変数に明示的な型指定を使用します。 最後の例では、 varを使用して暗黙的な型指定を使用する場合でも、同じ原則がどのように適用されるかを示します。

ソース データを変換しないクエリ

次の図は、データに対して変換を実行しない LINQ to Objects クエリ操作を示しています。 ソースには文字列のシーケンスが含まれており、クエリ出力も文字列のシーケンスです。

LINQ クエリのデータ型の関係を示す図。

  1. データ ソースの型引数によって、範囲変数の型が決まります。
  2. 選択したオブジェクトの型によって、クエリ変数の型が決まります。 ここで name は文字列です。 したがって、クエリ変数は IEnumerable<string>です。
  3. クエリ変数は、 foreach ステートメントで反復処理されます。 クエリ変数は文字列のシーケンスであるため、反復変数も文字列です。

ソース データを変換するクエリ

次の図は、データに対して単純な変換を実行する LINQ to SQL クエリ操作を示しています。 クエリは一連の Customer オブジェクトを入力として受け取り、結果の Name プロパティのみを選択します。 Nameは文字列であるため、クエリは文字列のシーケンスを出力として生成します。

データ型を変換するクエリを示す図。

  1. データ ソースの型引数によって、範囲変数の型が決まります。
  2. select ステートメントは、完全なCustomer オブジェクトではなく、Name プロパティを返します。 Nameは文字列であるため、custNameQueryの型引数はCustomerではなくstring
  3. custNameQueryは文字列のシーケンスであるため、foreach ループの反復変数もstringである必要があります。

次の図は、少し複雑な変換を示しています。 select ステートメントは、元のCustomer オブジェクトの 2 つのメンバーのみをキャプチャする匿名型を返します。

データ型を変換するより複雑なクエリを示す図。

  1. データ ソースの型引数は、常にクエリ内の範囲変数の型です。
  2. select ステートメントは匿名型を生成するため、クエリ変数はvarを使用して暗黙的に型指定する必要があります。
  3. クエリ変数の型は暗黙的であるため、 foreach ループ内の反復変数も暗黙的である必要があります。

コンパイラが型情報を推論する

クエリ操作の型リレーションシップを理解する必要がありますが、コンパイラにすべての作業を任せることができます。 キーワード var は、クエリ操作内の任意のローカル変数に使用できます。 次の図は、前に説明した数値 2 の例に似ています。 ただし、コンパイラはクエリ操作の各変数に厳密な型を提供します。

暗黙的な型指定を使用した型フローを示す図。

LINQ 型とジェネリック型 (C#)

LINQ クエリはジェネリック型に基づいています。 クエリの記述を開始する前に、ジェネリックに関する詳細な知識は必要ありません。 ただし、次の 2 つの基本的な概念を理解したい場合があります。

  1. List<T>などのジェネリック コレクション クラスのインスタンスを作成する場合は、"T" をリストが保持するオブジェクトの型に置き換えます。 たとえば、文字列のリストは List<string>として表され、 Customer オブジェクトのリストは List<Customer>として表されます。 ジェネリック リストは厳密に型指定されており、要素を Objectとして格納するコレクションに比して多くの利点があります。 List<string>Customerを追加しようとすると、コンパイル時にエラーが発生します。 実行時の型キャストを実行する必要がないため、ジェネリック コレクションを使用するのは簡単です。
  2. IEnumerable<T> は、 foreach ステートメントを使用してジェネリック コレクション クラスを列挙できるようにするインターフェイスです。 ジェネリック コレクション クラスは、IEnumerableをサポートするArrayListなど、非ジェネリック コレクション クラスと同様にIEnumerable<T>をサポートします。

ジェネリックの詳細については、「 ジェネリック」を参照してください。

LINQ クエリの IEnumerable<T> 変数

LINQ クエリ変数は、 IEnumerable<T> または IQueryable<T>などの派生型として型指定されます。 IEnumerable<Customer>として型指定されたクエリ変数が表示される場合は、クエリが実行されるときに、0 個以上のCustomer オブジェクトのシーケンスが生成されることを意味します。

IEnumerable<Customer> customerQuery = from cust in customers
                                      where cust.City == "London"
                                      select cust;

foreach (Customer customer in customerQuery)
{
    Console.WriteLine($"{customer.LastName}, {customer.FirstName}");
}

コンパイラがジェネリック型宣言を処理する

必要に応じて、 var キーワードを使用してジェネリック構文を回避できます。 var キーワードは、from句で指定されたデータ ソースを調べることで、クエリ変数の型を推論するようにコンパイラに指示します。 次の例では、前の例と同じコンパイル済みコードが生成されます。

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

foreach(var customer in customerQuery2)
{
    Console.WriteLine($"{customer.LastName}, {customer.FirstName}");
}

var キーワードは、変数の型が明確な場合や、グループ クエリによって生成されるジェネリック型など、入れ子になったジェネリック型を明示的に指定することが重要でない場合に便利です。 一般に、 varを使用する場合は、他のユーザーがコードを読みにくくする可能性があることを認識することをお勧めします。 詳細については、「 暗黙的に型指定されたローカル変数」を参照してください。