演练:用 Visual Basic 编写查询
本演练展示如何使用 Visual Basic 语言功能编写语言集成查询 (LINQ) 查询表达式。 本演练演示如何对“学生”对象列表创建查询、如何运行查询以及如何修改查询。 查询包含多个功能,包括对象初始值设定项、本地类型推断和匿名类型。
完成本演练后,你就可以继续查看自己感兴趣的特定 LINQ 提供程序的示例和文档。 LINQ 提供程序包括 LINQ to SQL、LINQ to DataSet 和 LINQ to XML。
创建项目
创建控制台应用程序项目
启动 Visual Studio。
在 “文件” 菜单上,指向 “新建” ,然后单击 “项目” 。
在“已安装的模板”列表中单击“Visual Basic”。
在项目类型列表中,单击“控制台应用程序”。 在“名称”框中,键入项目名称,然后单击“确定”。
这样就创建了一个项目。 默认情况下,它包含对 System.Core.dll 的引用。 此外,项目设计器 ->“引用”页 (Visual Basic) 上的“导入的命名空间”列表中包括 System.Linq 命名空间。
在“编译”页的“项目设计器 (Visual Basic)”中,确保“Option infer”设置为“打开”。
添加内存中的数据源
此演练中的查询的数据源是 Student
对象列表。 每个 Student
对象都包含名字、姓氏、年级以及在全体学生中的学业排名。
添加数据源
定义
Student
类,并创建类的实例的列表。重要
如何:创建项列表中提供了定义
Student
类以及创建在本演练示例中使用的列表所需的代码。 可以从中复制代码并粘贴在自己的项目中。 新代码将替换在创建项目时显示的代码。
向学生列表添加新学生
- 按照
getStudents
方法中的模式,将Student
的其他实例添加到列表。 添加学生的操作将引入对象初始值设定项。 有关详细信息,请参阅对象初始值设定项:命名类型和匿名类型。
创建查询
执行时,此部分中添加的查询将生成学业排名前十的学生列表。 由于查询每次都选择完整 Student
的对象,因此查询结果的类型为 IEnumerable(Of Student)
。 但是,查询定义中通常不指定查询的类型。 而是由编译器使用本地类型推断来确定该类型。 有关详细信息,请参阅本地类型推断。 查询的范围变量 currentStudent
用作对源 students
中每个 Student
实例的引用,提供对 students
中每个对象的属性的访问。
创建简单查询
在项目的
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
将鼠标指针悬停在代码中的
studentQuery
上,验证编译器分配的类型是否为IEnumerable(Of Student)
。
运行查询
变量 studentQuery
包含查询的定义,而不是运行查询的结果。 用于运行查询的一种典型机制就是 For Each
循环。 通过循环迭代变量,可访问返回的序列中的每个元素。 有关查询执行的详细信息,请参阅编写你的第一个 LINQ 查询。
运行查询
在项目中的查询下添加以下
For Each
循环。For Each studentRecord In studentQuery Console.WriteLine(studentRecord.Last & ", " & studentRecord.First) Next
将鼠标指针悬停在循环控制变量
studentRecord
上以查看其数据类型。studentRecord
的类型被推断为Student
,因为studentQuery
返回Student
实例的集合。按 CTRL + F5 生成并运行应用程序。 请注意控制台窗口中的结果。
修改查询
如果查询结果按指定顺序排列,则会更容易扫描。 可以基于任何可用字段对返回的序列进行排序。
对结果进行排序
在查询的
Where
语句和Select
语句之间添加以下Order By
子句。Order By
子句会根据每个学生的姓氏从 A 到 Z 的顺序对结果进行排序。Order By currentStudent.Last Ascending
若要按姓氏再按名字排序,请将这两个字段添加到查询中:
Order By currentStudent.Last Ascending, currentStudent.First Ascending
还能指定
Descending
以采用从 Z 到 A 的顺序。按 CTRL + F5 生成并运行应用程序。 请注意控制台窗口中的结果。
引入本地标识符
添加此部分中的代码,以在查询表达式中引入本地标识符。 本地标识符将保存中间结果。 在下面的示例中,
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
按 CTRL + F5 生成并运行应用程序。 请注意控制台窗口中的结果。
在 Select 子句中投影一个字段
添加此部分中的查询和
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
将鼠标指针悬停在代码中的
studentQuery3
上,验证分配的类型是否为IEnumerable(Of String)
。按 CTRL + F5 生成并运行应用程序。 请注意控制台窗口中的结果。
在 Select 子句中创建匿名类型
添加此部分中的代码,了解如何在查询中使用匿名类型。 如果希望从数据源返回多个字段而不是完整的记录(上文示例中的
currentStudent
记录)或单个字段(前文中的First
)时,可以在查询中使用它们。 可以在Select
子句中指定字段,而不是定义一个新的命名类型(其中包含你想要包括在结果中的字段),而编译器会创建一个匿名类型,并将这些字段作为其属性。 有关详细信息,请参阅匿名类型。下面的示例创建一个查询,该查询返回学业排名在 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
按 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 提供程序的文档和示例: