演练:在 Visual Basic 中编写查询

本演练演示如何使用 Visual Basic 语言功能编写 Language-Integrated 查询(LINQ)查询表达式。 该演练演示如何在 Student 对象列表上创建查询、如何运行查询以及如何修改查询。 这些查询包含多个功能,包括对象初始值设定项、本地类型推理和匿名类型。

完成本演练后,即可转到感兴趣的特定 LINQ 提供程序的示例和文档。 LINQ 提供程序包括 LINQ to SQL、LINQ to DataSet 和 LINQ to XML。

创建项目

创建控制台应用程序项目

  1. 启动 Visual Studio。

  2. “文件” 菜单上,指向 “新建” ,然后单击 “项目”

  3. “已安装的模板 ”列表中,单击 “Visual Basic”。

  4. 在项目类型列表中,单击 “控制台应用程序”。 在“ 名称 ”框中,键入项目的名称,然后单击“ 确定”。

    这样就创建了一个项目。 默认情况下,它包含对 System.Core.dll的引用。 此外,项目设计器(Visual Basic) 的“引用”页上的 “导入命名空间” 列表包括 System.Linq 命名空间。

  5. “编译”页的“项目设计器 (Visual Basic)”中,确保“Option infer”设置为“打开”

添加内存中的数据源

本演练中查询的数据源是对象列表 Student 。 每个 Student 对象都包含姓名、姓氏、年级以及在学生中的学术排名。

添加数据源

  • 定义类 Student ,并创建类的实例列表。

    重要

    Student中提供了定义类和创建演练示例中使用的列表所需的代码。 你可以从那里复制它并将其粘贴到项目中。 新代码将替换创建项目时显示的代码。

将新学生添加到学生列表

创建查询

执行后,在本部分中添加的查询将生成其学术排名排名前十的学生的列表。 由于查询每次都会选择完整的 Student 对象,因此查询结果的类型为 IEnumerable(Of Student)。 但是,查询类型通常未在查询定义中指定。 相反,编译器使用本地类型推理来确定类型。 有关详细信息,请参阅 本地类型推理。 查询的范围变量 currentStudent充当对源中每个 Student 实例的引用, students提供对每个 students对象属性的访问权限。

创建简单查询

  1. 在项目的 Main 方法中找到位置,标记如下:

    ' ****Paste query and query execution code from the walkthrough,
    ' ****or any code of your own, here in Main.
    

    复制以下代码并将其粘贴到其中。

    Dim studentQuery = From currentStudent In students
                       Where currentStudent.Rank <= 10
                       Select currentStudent
    
  2. 将鼠标指针 studentQuery 放在代码中,验证编译器分配的类型是否为 IEnumerable(Of Student)

运行查询

该变量 studentQuery 包含查询的定义,而不是运行查询的结果。 运行查询的典型机制是一个 For Each 循环。 通过循环迭代变量访问返回序列中的每个元素。 有关查询执行的详细信息,请参阅 编写第一个 LINQ 查询

运行查询

  1. 在项目中的查询下方添加以下 For Each 循环。

    For Each studentRecord In studentQuery
        Console.WriteLine(studentRecord.Last & ", " & studentRecord.First)
    Next
    
  2. 将鼠标指针悬停在循环控件变量 studentRecord 上以查看其数据类型。 推断studentRecord的类型为Student,因为studentQuery返回的是Student实例的集合。

  3. 按 Ctrl+F5 生成并运行应用程序。 记下控制台窗口中的结果。

修改查询

如果查询结果按指定顺序排列,则更容易扫描。 可以根据任何可用字段对返回的序列进行排序。

对结果进行排序

  1. Order By语句和Where查询语句之间添加以下Select子句。 该 Order By 子句将根据每个学生的姓氏按字母顺序从 A 到 Z 对结果进行排序。

    Order By currentStudent.Last Ascending
    
  2. 若要按姓氏排序,然后按名字排序,请将这两个字段添加到查询:

    Order By currentStudent.Last Ascending, currentStudent.First Ascending
    

    还可以指定 Descending 从 Z 到 A 的顺序。

  3. 按 Ctrl+F5 生成并运行应用程序。 记下控制台窗口中的结果。

引入本地标识符

  1. 添加本节中的代码,以在查询表达式中引入本地标识符。 本地标识符将保留中间结果。 在以下示例中,name 是一个用于保存学生名字和姓氏串联的标识符。 本地标识符可用于方便,或者通过存储原本需要多次计算的表达式的结果来提高性能。

    Dim studentQuery2 =
            From currentStudent In students
            Let name = currentStudent.Last & ", " & currentStudent.First
            Where currentStudent.Year = "Senior" And currentStudent.Rank <= 10
            Order By name Ascending
            Select currentStudent
    
    ' If you see too many results, comment out the previous
    ' For Each loop.
    For Each studentRecord In studentQuery2
        Console.WriteLine(studentRecord.Last & ", " & studentRecord.First)
    Next
    
  2. 按 Ctrl+F5 生成并运行应用程序。 记下控制台窗口中的结果。

在 Select 子句中投影一个字段

  1. 添加此部分中的查询和 For Each 循环,以创建一个查询,该查询生成一个序列,其元素与源中的元素不同。 在以下示例中,源是对象的集合 Student ,但只返回每个对象的一个成员:姓氏为 Garcia 的学生的名字。 因为 currentStudent.First 是字符串,因此 studentQuery3 返回的序列的数据类型是 IEnumerable(Of String)字符串序列。 像前面的示例一样,studentQuery3 的数据类型由编译器通过使用本地类型推断来确定。

    Dim studentQuery3 = From currentStudent In students
                        Where currentStudent.Last = "Garcia"
                        Select currentStudent.First
    
    ' If you see too many results, comment out the previous
    ' For Each loops.
    For Each studentRecord In studentQuery3
        Console.WriteLine(studentRecord)
    Next
    
  2. 将鼠标指针 studentQuery3 悬停在代码中,以验证分配的类型是否为 IEnumerable(Of String)

  3. 按 Ctrl+F5 生成并运行应用程序。 记下控制台窗口中的结果。

在 Select 子句中创建匿名类型

  1. 添加本节中的代码,以查看如何在查询中使用匿名类型。 如果要从数据源返回多个字段而不是完整记录(上一个示例中的记录)或单个字段(currentStudentFirst在上一部分),则可以在查询中使用它们。 无需定义包含要包含在结果中的字段的新命名类型,而是在子句中 Select 指定字段,编译器会创建一个匿名类型,并将这些字段用作其属性。 有关详细信息,请参阅匿名类型

    以下示例创建一个查询,该查询返回其学术排名在 1 到 10 之间的高级人员的姓名和排名,其学术排名顺序为 1 到 10。 在此示例中,由于studentQuery4子句返回的是匿名类型的实例,而匿名类型没有可用名称,因此必须推断Select的类型。

    Dim studentQuery4 =
            From currentStudent In students
            Where currentStudent.Year = "Senior" And currentStudent.Rank <= 10
            Order By currentStudent.Rank Ascending
            Select currentStudent.First, currentStudent.Last, currentStudent.Rank
    
    ' If you see too many results, comment out the previous
    ' For Each loops.
    For Each studentRecord In studentQuery4
        Console.WriteLine(studentRecord.Last & ", " & studentRecord.First &
                          ":  " & studentRecord.Rank)
    Next
    
  2. 按 Ctrl+F5 生成并运行应用程序。 记下控制台窗口中的结果。

其他示例

现在,你已了解基础知识,下面是一系列其他示例,用于说明 LINQ 查询的灵活性和功能。 每个示例前面都有有关其用途的简要说明。 将鼠标指针悬停在每个查询的查询结果变量上,以查看推断的类型。 使用For Each循环来生成结果。

' Find all students who are seniors.
Dim q1 = From currentStudent In students
         Where currentStudent.Year = "Senior"
         Select currentStudent

' Write a For Each loop to execute the query.
For Each q In q1
    Console.WriteLine(q.First & " " & q.Last)
Next

' Find all students with a first name beginning with "C".
Dim q2 = From currentStudent In students
         Where currentStudent.First.StartsWith("C")
         Select currentStudent

' Find all top ranked seniors (rank < 40).
Dim q3 = From currentStudent In students
         Where currentStudent.Rank < 40 And currentStudent.Year = "Senior"
         Select currentStudent

' Find all seniors with a lower rank than a student who 
' is not a senior.
Dim q4 = From student1 In students, student2 In students
         Where student1.Year = "Senior" And student2.Year <> "Senior" And
               student1.Rank > student2.Rank
         Select student1
         Distinct

' Retrieve the full names of all students, sorted by last name.
Dim q5 = From currentStudent In students
         Order By currentStudent.Last
         Select Name = currentStudent.First & " " & currentStudent.Last

' Determine how many students are ranked in the top 20.
Dim q6 = Aggregate currentStudent In students
         Where currentStudent.Rank <= 20
         Into Count()

' Count the number of different last names in the group of students.
Dim q7 = Aggregate currentStudent In students
         Select currentStudent.Last
         Distinct
         Into Count()

' Create a list box to show the last names of students.
Dim lb As New System.Windows.Forms.ListBox
Dim q8 = From currentStudent In students
         Order By currentStudent.Last
         Select currentStudent.Last Distinct

For Each nextName As String In q8
    lb.Items.Add(nextName)
Next

' Find every process that has a lowercase "h", "l", or "d" in its name.
Dim letters() As String = {"h", "l", "d"}
Dim q9 = From proc In System.Diagnostics.Process.GetProcesses,
         letter In letters
         Where proc.ProcessName.Contains(letter)
         Select proc

For Each proc In q9
    Console.WriteLine(proc.ProcessName & ", " & proc.WorkingSet64)
Next

其他信息

熟悉使用查询的基本概念后,即可阅读你感兴趣的特定类型的 LINQ 提供程序的文档和示例:

另请参阅