次の方法で共有


クエリ操作での型リレーションシップ (Visual Basic)

Language-Integrated クエリ (LINQ) クエリ操作で使用される変数は厳密に型指定されており、相互に互換性がある必要があります。 厳密な型指定は、データ ソース、クエリ自体、およびクエリ実行で使用されます。 次の図は、LINQ クエリの説明に使用される用語を示しています。 クエリの各部分の詳細については、「 基本的なクエリ操作 (Visual Basic)」を参照してください。

要素が強調表示された擬似コード クエリを示すスクリーンショット。

クエリ内の範囲変数の型は、データ ソース内の要素の型と互換性がある必要があります。 クエリ変数の型は、 Select 句で定義されているシーケンス要素と互換性がある必要があります。 最後に、シーケンス要素の型は、クエリを実行する For Each ステートメントで使用されるループ制御変数の型と互換性がある必要もあります。 この厳密な型指定により、コンパイル時の型エラーの識別が容易になります。

Visual Basic では、ローカル型推論 (暗黙的な型指定とも呼ばれます) を実装することで、厳密 な型指定が便利になります。 この機能は前の例で使用されており、LINQ のサンプルとドキュメント全体で使用されています。 Visual Basic では、ローカル型の推論は、As句のないDim ステートメントを使用するだけで実現されます。 次の例では、 city は文字列として厳密に型指定されています。

Dim city = "Seattle"

ローカル型推論は、 Option InferOn に設定されている場合にのみ機能します。 詳細については、「 Option Infer ステートメント」を参照してください。

ただし、クエリでローカル型推論を使用する場合でも、データ ソース内の変数、クエリ変数、およびクエリ実行ループの間には、同じ型リレーションシップが存在します。 LINQ クエリを記述する場合や、ドキュメントのサンプルとコード例を使用する場合は、これらの型リレーションシップの基本的な理解を深めると便利です。

データ ソースから返される型と一致しない範囲変数に明示的な型を指定することが必要な場合があります。 範囲変数の型は、 As 句を使用して指定できます。 ただし、変換が 縮小 変換で、 Option StrictOn に設定されている場合は、エラーが発生します。 そのため、データ ソースから取得した値に対して変換を実行することをお勧めします。 Cast メソッドを使用して、データ ソースの値を明示的な範囲変数型に変換できます。 Select句で選択した値を、範囲変数の型とは異なる明示的な型にキャストすることもできます。 これらの点を次のコードに示します。

Dim numbers1() As Integer = {1, 2, 4, 16, 32, 64}
Dim numbers2() As Double = {5.0#, 10.0#, 15.0#}

' This code does not result in an error.
Dim numberQuery1 = From n As Integer In numbers1 Where n > 5

' This code results in an error with Option Strict set to On. The type Double
' cannot be implicitly cast as type Integer.
Dim numberQuery2 = From n As Integer In numbers2 Where n > 5

' This code casts the values in the data source to type Integer. The type of
' the range variable is Integer.
Dim numberQuery3 = From n In numbers2.Cast(Of Integer)() Where n > 5

' This code returns the value of the range variable converted to Integer. The type of
' the range variable is Double.
Dim numberQuery4 = From n In numbers2 Where n > 5 Select CInt(n)

ソース データの要素全体を返すクエリ

次の例は、ソース データから選択された要素のシーケンスを返す LINQ クエリ操作を示しています。 ソース ( names) には文字列の配列が含まれており、クエリ出力は文字 M で始まる文字列を含むシーケンスです。

Dim names = {"John", "Rick", "Maggie", "Mary"}
Dim mNames = From name In names
             Where name.IndexOf("M") = 0
             Select name

For Each nm In mNames
    Console.WriteLine(nm)
Next

これは次のコードと同じですが、はるかに短く、簡単に記述できます。 Visual Basic では、クエリでのローカル型の推論への依存が推奨されるスタイルです。

Dim names2 = {"John", "Rick", "Maggie", "Mary"}
Dim mNames2 As IEnumerable(Of String) =
    From name As String In names
    Where name.IndexOf("M") = 0
    Select name

For Each nm As String In mNames
    Console.WriteLine(nm)
Next

次のリレーションシップは、型が暗黙的に決定されるか明示的に決定されるかに関係なく、前のコード例の両方に存在します。

  1. データ ソースの要素の型 ( names) は、クエリ内の範囲変数 nameの型です。

  2. 選択したオブジェクトの型 ( name) によって、クエリ変数の型 ( mNames) が決まります。 ここで name は文字列であるため、クエリ変数は Visual Basic の IEnumerable(Of String) です。

  3. mNamesで定義されたクエリは、For Each ループで実行されます。 このループは、クエリの実行結果を反復処理します。 mNames、それが実行されると文字列のシーケンスを返すので、ループ反復変数nmも文字列です。

選択した要素から 1 つのフィールドを返すクエリ

次の例は、データ ソースから選択された各要素の 1 つの部分のみを含むシーケンスを返す LINQ to SQL クエリ操作を示しています。 クエリは、 Customer オブジェクトのコレクションをデータ ソースとして受け取り、結果の Name プロパティのみを投影します。 顧客名は文字列であるため、クエリは文字列のシーケンスを出力として生成します。

' Method GetTable returns a table of Customer objects.
Dim customers = db.GetTable(Of Customer)()
Dim custNames = From cust In customers
                Where cust.City = "London"
                Select cust.Name

For Each custName In custNames
    Console.WriteLine(custName)
Next

変数間のリレーションシップは、より単純な例のようなものです。

  1. データ ソースの要素の型 ( customers) は、クエリ内の範囲変数 custの型です。 この例では、その型は Customer

  2. Select ステートメントは、オブジェクト全体ではなく、各Customer オブジェクトのName プロパティを返します。 Nameは文字列であるため、クエリ変数custNamesは、Customerではなく IEnumerable(Of String) になります。

  3. custNamesは文字列のシーケンスを表すので、For Each ループの反復変数 (custName) は文字列である必要があります。

ローカル型の推論がない場合、次の例に示すように、前の例では書き込みと理解が煩雑になります。

' Method GetTable returns a table of Customer objects.
 Dim customers As Table(Of Customer) = db.GetTable(Of Customer)()
 Dim custNames As IEnumerable(Of String) =
     From cust As Customer In customers
     Where cust.City = "London"
     Select cust.Name

 For Each custName As String In custNames
     Console.WriteLine(custName)
 Next

匿名型を必要とするクエリ

次の例は、より複雑な状況を示しています。 前の例では、すべての変数の型を明示的に指定するのは不便でした。 この例では不可能です。 データ ソースから Customer 要素全体、または各要素から 1 つのフィールドを選択する代わりに、このクエリの Select 句は、元の Customer オブジェクトの 2 つのプロパティ ( NameCity) を返します。 Select句に応答して、コンパイラはこれら 2 つのプロパティを含む匿名型を定義します。 For Each ループでnameCityQueryを実行した結果は、新しい匿名型のインスタンスのコレクションです。 匿名型には使用可能な名前がないため、 nameCityQuery の型や custInfo を明示的に指定することはできません。 つまり、匿名型では、IEnumerable(Of String)Stringの代わりに使用する型名はありません。 詳細については、「 匿名型」を参照してください。

' Method GetTable returns a table of Customer objects.
Dim customers = db.GetTable(Of Customer)()
Dim nameCityQuery = From cust In customers
                    Where cust.City = "London"
                    Select cust.Name, cust.City

For Each custInfo In nameCityQuery
    Console.WriteLine(custInfo.Name)
Next

前の例のすべての変数に型を指定することはできませんが、リレーションシップは変わりません。

  1. データ ソース内の要素の型は、クエリ内の範囲変数の型です。 この例では、 custCustomerのインスタンスです。

  2. Select ステートメントは匿名型を生成するため、クエリ変数 nameCityQueryは匿名型として暗黙的に型指定する必要があります。 匿名型には使用可能な名前がないため、明示的に指定することはできません。

  3. For Each ループ内の反復変数の型は、手順 2 で作成した匿名型です。 この型には使用可能な名前がないため、ループ反復変数の型を暗黙的に決定する必要があります。

こちらも参照ください