クエリ操作での型の関係 (Visual Basic)
統合言語クエリ (LINQ) のクエリ操作で使用される変数は厳密に型指定されており、互いに互換性がなければなりません。 厳密な型指定は、データ ソースで使用されるほか、クエリそのものや、クエリの実行でも使用されます。 次の図は、LINQ クエリの説明で用いられる用語を示したものです。 クエリの構成要素について詳しくは、「基本的なクエリ操作 (Visual Basic)」を参照してください。
クエリの範囲変数の型には、データ ソース内の要素の型との互換性が必要です。 クエリ変数の型は、Select
句で定義されているシーケンス要素と互換性がある必要があります。 最後に、シーケンス要素の型も、クエリを実行する For Each
ステートメントで使用されるループ制御変数の型と互換性がある必要があります。 この厳密な型指定によって、コンパイル時に発生する型エラーが特定しやすくなります。
Visual Basic は、ローカル型推論 ("暗黙の型指定" とも呼ばれます) を実装することで、厳密な型指定の利便性を高めています。 その機能は、前の例で使用されているほか、LINQ のサンプルやドキュメントの至る所で目にすることができます。 Visual Basic では、単に As
句のない Dim
ステートメントを使用すれば、ローカル型推論が行われます。 次の例の city
は、文字列として厳密に型指定されます。
Dim city = "Seattle"
Note
ローカル型推論が機能するのは、Option Infer
が On
に設定されているときだけです。 詳細については、「Option Infer ステートメント」を参照してください。
ただし、クエリでローカル型推論を使用した場合でも、データ ソース内の変数、クエリ変数、クエリの実行ループの間に存在する型の関係は同じです。 こうした型の関係についての基本的な知識は、LINQ クエリを記述するときや、ドキュメント内のサンプルまたはコード例を扱うときに役立ちます。
データ ソースから返された型と一致しない範囲変数には、明示的な型の指定が必要になる場合もあります。 範囲変数の型は、As
句を使用して指定できます。 ただし、変換が縮小変換で、なおかつ Option Strict
が On
に設定されているとエラーになります。 したがって変換は、データ ソースから取得された値に対して実行することをお勧めします。 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
型の指定が暗黙的であれ明示的であれ、前出のコード例には、どちらも次の関係が存在します。
データ ソース (
names
) に含まれる要素の型は、クエリの範囲変数 (name
) の型です。選択したオブジェクト (
name
) の型によって、クエリ変数 (mNames
) の型が決まります。 ここではname
が文字列であるため、クエリ変数は Visual Basic の IEnumerable(Of String) となります。mNames
で定義されたクエリは、For Each
ループで実行されます。 このループによって、クエリの実行結果が反復処理されます。mNames
は、実行されたときに文字列のシーケンスを返すため、ループの反復変数 (nm
) も文字列になります。
選択された要素から 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
単純な方の例と同様、変数間の関係は次のようになります。
データ ソース (
customers
) に含まれる要素の型は、クエリの範囲変数 (cust
) の型です。 この例では、Customer
型が該当します。Select
ステートメントから返されるのは、オブジェクト全体ではなく、各Customer
オブジェクトのName
プロパティです。Name
は文字列であるため、この場合もクエリ変数 (custNames
) は IEnumerable(Of String) であって、Customer
ではありません。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
匿名型を必要とするクエリ
次に示したのは、より複雑な状況の例です。 前の例では、すべての変数に対して明示的に型を指定するのは、容易ではありませんでした。 この例では、それが不可能となります。 このクエリの Select
句は、データ ソースから Customer
要素全体を選択したり、各要素から 1 つのフィールドだけを選択したりするのではなく、元の Customer
オブジェクトから Name
と City
の 2 つのプロパティを返します。 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
前の例のすべての変数に型を指定することはできませんが、その関係は変わりません。
この場合も、データ ソースに含まれる要素の型は、クエリの範囲変数の型です。 この例の
cust
は、Customer
のインスタンスです。Select
ステートメントによって匿名型が生成されるため、クエリ変数 (nameCityQuery
) は、匿名型として暗黙的に型指定する必要があります。 匿名型は、有効な名前を持たないため、明示的に指定することはできません。For Each
ループにおける反復変数の型は、手順 2. で作成した匿名型です。 この型には有効な名前がないため、ループの反復変数の型は、必然的に暗黙的に指定されます。
関連項目
.NET