查询作中的类型关系 (Visual Basic)

Language-Integrated 查询(LINQ)查询操作中使用的变量是强类型化的,必须彼此兼容。 强类型化在数据源、查询本身和查询执行中使用。 下图标识用于描述 LINQ 查询的术语。 有关查询的各个部分的详细信息,请参阅基本查询作(Visual Basic)。

伪代码查询的屏幕截图,其中元素被突出显示。

查询中范围变量的类型必须与数据源中元素的类型兼容。 查询变量的类型必须与子句中 Select 定义的序列元素兼容。 最后,序列元素的类型还必须与执行查询的语句中使用的 For Each 循环控制变量的类型兼容。 这种强类型化有助于在编译时识别类型错误。

Visual Basic 通过实现本地类型推理(也称为 隐式键入)来方便强键入。 此功能在上一示例中使用,你将在 LINQ 示例和文档中看到该功能使用。 在 Visual Basic 中,本地类型推理只需使用没有Dim子句的As语句即可完成。 在以下示例中,city 被严格定义为字符串类型。

Dim city = "Seattle"

注释

本地类型推断仅在 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

在前面的两个代码示例中都存在以下关系,无论类型是隐式确定还是显式确定。

  1. 数据源 names中元素的类型是查询中的范围变量 name的类型。

  2. 所选对象的类型, name确定查询变量 mNames的类型。 下面是 name 一个字符串,因此查询变量在 Visual Basic 中为 IEnumerable(Of String)。

  3. mNames循环中执行For Each中定义的查询。 循环遍历执行查询的结果。 因为mNames在执行时将返回一系列字符串,因此循环迭代变量nm也是一个字符串。

从所选元素返回一个字段的查询

以下示例演示了一个 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 语句返回 Name 每个 Customer 对象的属性,而不是整个对象。 因为 Name 是字符串,查询变量 custNames 将再次为 IEnumerable(Of String),而不是 Customer

  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子句返回原始Select对象的两个属性:CustomerName,而不是从数据源中选择整个City元素或每个元素的单个字段。 为了响应子 Select 句,编译器定义一个包含这两个属性的匿名类型。 在nameCityQuery循环中执行For Each的结果是新的匿名类型的实例集合。 由于匿名类型没有可用名称,因此无法显式指定nameCityQuerycustInfo的类型。 也就是说,使用匿名类型时,没有类型名称可用于替代 String 中的 IEnumerable(Of 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. 数据源中元素的类型再次是查询中范围变量的类型。 在此示例中, cust 是一 Customer个实例。

  2. Select由于该语句生成匿名类型,因此查询变量nameCityQuery必须隐式键入为匿名类型。 匿名类型没有可用名称,因此无法显式指定。

  3. 循环中 For Each 迭代变量的类型是在步骤 2 中创建的匿名类型。 由于类型没有可用名称,因此必须隐式确定循环迭代变量的类型。

另请参阅