VB Visual Basic 2026 中文教程
第 31 课 · 用更简洁的方式查询对象

使用 LINQ 查询数据

前面的课程已经能够从数据库取得 List(Of Student)。如果现在要找出高分学生、按姓名排序、只取得姓名、计算平均分,当然可以写 For Each 循环,但 LINQ 能让这些查询更简洁、更接近“描述你想要什么”的写法。本课将直接对 List(Of Student) 使用 LINQ,并把结果显示到 DataGridView。

环境:Visual Studio 2026 · VB.NET · .NET 10数据来源:List(Of Student)实作:StudentLinqExplorer2026

本课学习目标

  • 理解 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,再进行排序或统计。

LINQ

用 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。

s
当前正在检查的 Student 对象。
s.Score
读取当前学生的成绩。
>= 80D
条件结果是 True 或 False。
Where
只保留条件为 True 的对象。

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)
Select 不一定返回 Student 它可以把原来的 Student 转换成 String、数值,甚至新的匿名对象。

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}"
空集合要注意 如果集合完全没有记录,直接 Average() 会发生异常。实际程序应先检查 Any()

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)
List(Of Student) Where Course Where Score OrderByDescending 结果

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。

初学阶段可以这样记 需要一个稳定的结果列表时使用 ToList();只是继续组合查询时可以先保留 LINQ 查询。

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)

表达的意思相近。

Method Syntax

Where、Select、OrderBy 等方法链,和 .NET API 风格一致,本课主要使用。

Query Syntax

From、Where、Order By、Select,较接近 SQL 阅读方式。

31.18 实作:Student LINQ Explorer 2026

建立新的 Windows Forms 项目:

StudentLinqExplorer2026

继续使用 Student 类和 StudentRepository,并让 Repository 先返回:

List(Of Student)
Student LINQ Explorer 2026
最低成绩:
80
课程:
Visual Basic 2026
显示全部 高分学生 按成绩排序 课程筛选 统计
Student ID姓名课程成绩
4李华Visual Basic 202693.00
1李明Visual Basic 202688.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 查询?

SQL

适合让数据库服务器直接筛选大量数据,避免把所有记录先加载到应用。

LINQ to Objects

适合已经在内存中的 List(Of Student) 做进一步筛选、排序和统计。

不要把整张巨大数据库表全部加载后才用 LINQ 筛选 如果数据库有大量数据,优先让 SQL WHERE 在数据库端减少数据量,再对取得的对象做必要的 LINQ 处理。

31.26 初学者常见错误

错误 1

忘记 ToList 就以为没有结果

很多 LINQ 查询是延迟执行;真正枚举时才产生结果。

错误 2

空集合直接 Average

先用 Any() 检查是否有数据。

错误 3

混淆 Where 与 Select

Where 负责筛选;Select 负责转换或投影。

错误 4

数据库大量数据全部拉进 List

大数据筛选应优先放在 SQL WHERE 中完成。

31.27 小练习

  1. 使用 LINQ 找出成绩最低的学生。
  2. 使用 Any() 判断是否存在不及格学生。
  3. 使用 FirstOrDefault() 根据 StudentID 查找学生。
  4. 使用 Select() 只显示姓名和成绩。
  5. 按 Course 排序,再在同一课程中按 Score 降序。
  6. 计算成绩 ≥ 80 的学生人数。
  7. 建立一个“Top 3”按钮,使用 OrderByDescending(...).Take(3)

本课复习

  1. LINQ 的主要用途是什么?
  2. Where() 做什么?
  3. OrderBy()OrderByDescending() 有什么不同?
  4. Select() 与 Where() 有什么不同?
  5. FirstOrDefault() 找不到记录时会怎样?
  6. Any() 适合解决什么问题?
  7. 空集合为什么不能直接 Average()?
  8. Lambda 中的 s 代表什么?
  9. ToList() 有什么作用?
  10. 什么时候应该用 SQL WHERE,什么时候适合用 LINQ to Objects?