本课学习目标
- 理解 LINQ 的用途。
- 使用 Where() 筛选对象。
- 使用 OrderBy() 与 OrderByDescending() 排序。
- 使用 Select() 投影数据。
- 使用 FirstOrDefault() 找第一笔匹配记录。
- 使用 Any() 判断是否存在符合条件的对象。
- 使用 Count() 统计数量。
- 使用 Average() 计算平均值。
- 理解 Lambda 表达式中的参数,例如 Function(s)。
- 理解延迟执行与 ToList() 的基本区别。
- 完成 StudentLinqExplorer2026。
31.1 什么是 LINQ?
LINQ 是 Language Integrated Query。它让我们可以直接在 VB.NET 中查询集合对象,而不需要每次都手工写循环。
自己 For Each、If 判断、Add 到新 List,再进行排序或统计。
用 Where、OrderBy、Select 等方法描述“我要哪些数据”。
31.2 准备 Student 数据
Dim students As New List(Of Student) From {
New Student(1, "李明", "Visual Basic 2026", 88D),
New Student(2, "王芳", "C# Programming", 76D),
New Student(3, "陈伟", "Python Programming", 91D),
New Student(4, "李华", "Visual Basic 2026", 93D),
New Student(5, "张敏", "C# Programming", 67D)
}
31.3 Where:筛选数据
找出成绩至少 80 的学生:
Dim highScoreStudents =
students.Where(
Function(s)
Return s.Score >= 80D
End Function)
也可以写成较短形式:
Dim highScoreStudents =
students.Where(
Function(s) s.Score >= 80D)
31.4 Function(s) 是什么?
这里:
Function(s) s.Score >= 80D
是 Lambda 表达式。可以理解为一个很短、没有单独命名的小 Function。
31.5 OrderBy:升序排序
按姓名排序:
Dim sortedStudents =
students.OrderBy(
Function(s) s.Name)
按成绩从低到高:
Dim sortedByScore =
students.OrderBy(
Function(s) s.Score)
31.6 OrderByDescending:降序排序
Dim topStudents =
students.OrderByDescending(
Function(s) s.Score)
最高分会排在最前面。
31.7 ThenBy:第二排序条件
Dim sortedStudents =
students.
OrderBy(
Function(s) s.Course).
ThenByDescending(
Function(s) s.Score)
先按 Course 排序,同一个课程内再按 Score 高到低排序。
31.8 Select:只取需要的资料
只取得姓名:
Dim names =
students.Select(
Function(s) s.Name)
取得姓名与成绩组成的新对象:
Dim summary =
students.Select(
Function(s)
Return New With {
.StudentName = s.Name,
.StudentScore = s.Score
}
End Function)
31.9 FirstOrDefault:找第一笔匹配资料
Dim student As Student =
students.FirstOrDefault(
Function(s)
Return s.StudentID = 3
End Function)
然后检查:
If student Is Nothing Then
MessageBox.Show(
"找不到学生。")
Else
MessageBox.Show(
student.Name)
End If
31.10 Any:有没有符合条件的记录?
Dim hasExcellentStudent As Boolean =
students.Any(
Function(s) s.Score >= 90D)
如果至少一位学生成绩达到 90,结果就是 True。
31.11 Count:统计数量
全部学生人数:
Dim total As Integer =
students.Count()
成绩及格人数:
Dim passCount As Integer =
students.Count(
Function(s) s.Score >= 50D)
31.12 Average:计算平均分
Dim averageScore As Decimal =
students.Average(
Function(s) s.Score)
显示:
lblAverage.Text =
$"平均成绩:{averageScore:N2}"
31.13 安全计算平均分
If students.Any() Then
Dim averageScore As Decimal =
students.Average(
Function(s) s.Score)
lblAverage.Text =
$"平均成绩:{averageScore:N2}"
Else
lblAverage.Text =
"平均成绩:无资料"
End If
31.14 链式 LINQ 查询
只看 VB 课程、高于 80 分,并按成绩从高到低:
Dim results =
students.
Where(
Function(s)
Return s.Course =
"Visual Basic 2026"
End Function).
Where(
Function(s)
Return s.Score >= 80D
End Function).
OrderByDescending(
Function(s) s.Score)
31.15 为什么经常看到 ToList()?
Dim results As List(Of Student) =
students.
Where(
Function(s) s.Score >= 80D).
ToList()
Where() 返回的是可枚举查询结果;ToList() 会立即执行查询并把结果建立成新的 List(Of Student)。
31.16 延迟执行的基本概念
Dim query =
students.Where(
Function(s) s.Score >= 80D)
这时 query 主要代表“怎样查询”。真正遍历它时才会计算结果。
如果使用:
Dim results =
query.ToList()
就会在这一刻把结果真正取出来并保存到新的 List。
31.17 LINQ 查询语法
VB.NET 也支持 SQL 风格的查询写法:
Dim results =
From student In students
Where student.Score >= 80D
Order By student.Score Descending
Select student
它与方法语法:
students.
Where(
Function(s) s.Score >= 80D).
OrderByDescending(
Function(s) s.Score)
表达的意思相近。
Where、Select、OrderBy 等方法链,和 .NET API 风格一致,本课主要使用。
From、Where、Order By、Select,较接近 SQL 阅读方式。
31.18 实作:Student LINQ Explorer 2026
建立新的 Windows Forms 项目:
StudentLinqExplorer2026
继续使用 Student 类和 StudentRepository,并让 Repository 先返回:
List(Of Student)
| Student ID | 姓名 | 课程 | 成绩 |
|---|---|---|---|
| 4 | 李华 | Visual Basic 2026 | 93.00 |
| 1 | 李明 | Visual Basic 2026 | 88.00 |
31.19 Form1 保存原始学生列表
Private ReadOnly repository As New StudentRepository()
Private students As New List(Of Student)()
Form.Load:
Private Sub Form1_Load(
sender As Object,
e As EventArgs) Handles MyBase.Load
Try
students =
repository.GetAll()
ShowStudents(
students)
Catch ex As Exception
MessageBox.Show(
ex.Message,
"错误",
MessageBoxButtons.OK,
MessageBoxIcon.Error)
End Try
End Sub
31.20 ShowStudents 共用方法
Private Sub ShowStudents(
data As IEnumerable(Of Student))
dgvStudents.DataSource =
Nothing
dgvStudents.DataSource =
data.ToList()
lblCount.Text =
$"记录数:{data.Count()}"
End Sub
31.21 高分学生按钮
Private Sub btnHighScore_Click(
sender As Object,
e As EventArgs) Handles btnHighScore.Click
Dim minScore As Decimal
If Not Decimal.TryParse(
txtMinScore.Text,
minScore) Then
MessageBox.Show(
"请输入有效最低成绩。")
Exit Sub
End If
Dim results =
students.
Where(
Function(s)
Return s.Score >=
minScore
End Function).
OrderByDescending(
Function(s) s.Score)
ShowStudents(
results)
End Sub
31.22 按课程筛选
Private Sub btnCourse_Click(
sender As Object,
e As EventArgs) Handles btnCourse.Click
Dim course As String =
txtCourse.Text.Trim()
Dim results =
students.
Where(
Function(s)
Return s.Course.Equals(
course,
StringComparison.OrdinalIgnoreCase)
End Function).
OrderBy(
Function(s) s.Name)
ShowStudents(
results)
End Sub
31.23 按成绩高到低排序
Private Sub btnSortScore_Click(
sender As Object,
e As EventArgs) Handles btnSortScore.Click
Dim results =
students.
OrderByDescending(
Function(s) s.Score)
ShowStudents(
results)
End Sub
31.24 LINQ 统计按钮
Private Sub btnStatistics_Click(
sender As Object,
e As EventArgs) Handles btnStatistics.Click
If Not students.Any() Then
MessageBox.Show(
"目前没有学生资料。")
Exit Sub
End If
Dim total As Integer =
students.Count()
Dim passCount As Integer =
students.Count(
Function(s)
Return s.Score >= 50D
End Function)
Dim averageScore As Decimal =
students.Average(
Function(s) s.Score)
Dim highestScore As Decimal =
students.Max(
Function(s) s.Score)
MessageBox.Show(
$"学生人数:{total}" &
Environment.NewLine &
$"及格人数:{passCount}" &
Environment.NewLine &
$"平均成绩:{averageScore:N2}" &
Environment.NewLine &
$"最高成绩:{highestScore:N2}",
"LINQ 统计",
MessageBoxButtons.OK,
MessageBoxIcon.Information)
End Sub
31.25 SQL 查询还是 LINQ 查询?
适合让数据库服务器直接筛选大量数据,避免把所有记录先加载到应用。
适合已经在内存中的 List(Of Student) 做进一步筛选、排序和统计。
31.26 初学者常见错误
忘记 ToList 就以为没有结果
很多 LINQ 查询是延迟执行;真正枚举时才产生结果。
空集合直接 Average
先用 Any() 检查是否有数据。
混淆 Where 与 Select
Where 负责筛选;Select 负责转换或投影。
数据库大量数据全部拉进 List
大数据筛选应优先放在 SQL WHERE 中完成。
31.27 小练习
- 使用 LINQ 找出成绩最低的学生。
- 使用 Any() 判断是否存在不及格学生。
- 使用 FirstOrDefault() 根据 StudentID 查找学生。
- 使用 Select() 只显示姓名和成绩。
- 按 Course 排序,再在同一课程中按 Score 降序。
- 计算成绩 ≥ 80 的学生人数。
- 建立一个“Top 3”按钮,使用 OrderByDescending(...).Take(3)。
本课复习
- LINQ 的主要用途是什么?
- Where() 做什么?
- OrderBy() 与 OrderByDescending() 有什么不同?
- Select() 与 Where() 有什么不同?
- FirstOrDefault() 找不到记录时会怎样?
- Any() 适合解决什么问题?
- 空集合为什么不能直接 Average()?
- Lambda 中的 s 代表什么?
- ToList() 有什么作用?
- 什么时候应该用 SQL WHERE,什么时候适合用 LINQ to Objects?