クエリを効果的に記述するには、完全なクエリ操作の変数の型がすべて相互にどのように関連しているかを理解する必要があります。 これらの関係を理解すれば、ドキュメント内の LINQ サンプルとコード例をより簡単に理解できます。 さらに、 var
を使用して変数を暗黙的に型指定するとどうなるかを理解できます。
LINQ クエリ操作は、データ ソース、クエリ自体、およびクエリ実行で厳密に型指定されます。 クエリ内の変数の型は、データ ソース内の要素の型と、 foreach
ステートメントの反復変数の型と互換性がある必要があります。 この厳密な型指定により、ユーザーがエラーを検出する前に修正できる場合に、コンパイル時に型エラーがキャッチされます。
これらの型リレーションシップを示すために、次の例のほとんどは、すべての変数に明示的な型指定を使用します。 最後の例では、 var
を使用して暗黙的な型指定を使用する場合でも、同じ原則がどのように適用されるかを示します。
ソース データを変換しないクエリ
次の図は、データに対して変換を実行しない LINQ to Objects クエリ操作を示しています。 ソースには文字列のシーケンスが含まれており、クエリ出力も文字列のシーケンスです。
- データ ソースの型引数によって、範囲変数の型が決まります。
- 選択したオブジェクトの型によって、クエリ変数の型が決まります。 ここで
name
は文字列です。 したがって、クエリ変数はIEnumerable<string>
です。 - クエリ変数は、
foreach
ステートメントで反復処理されます。 クエリ変数は文字列のシーケンスであるため、反復変数も文字列です。
ソース データを変換するクエリ
次の図は、データに対して単純な変換を実行する LINQ to SQL クエリ操作を示しています。 クエリは一連の Customer
オブジェクトを入力として受け取り、結果の Name
プロパティのみを選択します。
Name
は文字列であるため、クエリは文字列のシーケンスを出力として生成します。
- データ ソースの型引数によって、範囲変数の型が決まります。
-
select
ステートメントは、完全なCustomer
オブジェクトではなく、Name
プロパティを返します。Name
は文字列であるため、custNameQuery
の型引数はCustomer
ではなくstring
。 -
custNameQuery
は文字列のシーケンスであるため、foreach
ループの反復変数もstring
である必要があります。
次の図は、少し複雑な変換を示しています。
select
ステートメントは、元のCustomer
オブジェクトの 2 つのメンバーのみをキャプチャする匿名型を返します。
- データ ソースの型引数は、常にクエリ内の範囲変数の型です。
-
select
ステートメントは匿名型を生成するため、クエリ変数はvar
を使用して暗黙的に型指定する必要があります。 - クエリ変数の型は暗黙的であるため、
foreach
ループ内の反復変数も暗黙的である必要があります。
コンパイラが型情報を推論する
クエリ操作の型リレーションシップを理解する必要がありますが、コンパイラにすべての作業を任せることができます。 キーワード var は、クエリ操作内の任意のローカル変数に使用できます。 次の図は、前に説明した数値 2 の例に似ています。 ただし、コンパイラはクエリ操作の各変数に厳密な型を提供します。
LINQ 型とジェネリック型 (C#)
LINQ クエリはジェネリック型に基づいています。 クエリの記述を開始する前に、ジェネリックに関する詳細な知識は必要ありません。 ただし、次の 2 つの基本的な概念を理解したい場合があります。
-
List<T>などのジェネリック コレクション クラスのインスタンスを作成する場合は、"T" をリストが保持するオブジェクトの型に置き換えます。 たとえば、文字列のリストは
List<string>
として表され、Customer
オブジェクトのリストはList<Customer>
として表されます。 ジェネリック リストは厳密に型指定されており、要素を Objectとして格納するコレクションに比して多くの利点があります。List<string>
にCustomer
を追加しようとすると、コンパイル時にエラーが発生します。 実行時の型キャストを実行する必要がないため、ジェネリック コレクションを使用するのは簡単です。 -
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
を使用する場合は、他のユーザーがコードを読みにくくする可能性があることを認識することをお勧めします。 詳細については、「 暗黙的に型指定されたローカル変数」を参照してください。
.NET