C# LINQ 全面解析

101 阅读6分钟

image.png 本文收录于《控制工程自学指南》上位机篇,© 2025 SAJalon


前言

LINQ(Language Integrated Query,语言集成查询)是 C# 中一个强大的特性,它允许开发者使用类似 SQL 的语法来查询各种数据源。本文将通过简单易懂的案例,全面梳理 LINQ 的使用方法和底层原理。

什么是 LINQ?

LINQ(Language Integrated Query) 是 .NET Framework 3.5 引入的一个特性,它将查询功能直接集成到 C# 语言中。通过 LINQ,我们可以用统一的语法查询内存中的数组、集合、数据库、XML 等其中各种数据源,凡是可枚举的对象皆可使用LINQ

LINQ 的核心概念

  • 查询表达式:使用类似 SQL 的语法编写的查询语句

  • 方法语法:使用扩展方法链式调用的方式

  • 延迟执行:大多数 LINQ 操作不会立即执行,而是在枚举时才执行

  • 立即执行:某些操作会立即执行并返回结果

LINQ 的优势

  • 统一语法: 无论查询什么类型的数据,都使用相同的语法

  • 类型安全: 编译时检查,减少运行时错误

  • 延迟执行: 提高查询性能

效果比较

非LINQ方式 输出数组中 大于等于2 的元素:

using System;

class Program
{
    static void Main()
    {
        var collection = new int[] { 1, 2, 2, 3, 4, 6 };
        for (int i = 0; i < collection.Length; i++)
        {
            if (collection[i] >= 2)
            {
                 Console.WriteLine(collection[i]);
            }
        }
    }
}

以 LINQ方式 输出数组中 大于等于2 的元素:

using System;

class Program
{
    static void Main()
    {
        var collection = new int[] { 1, 2, 2, 3, 4, 6 };
        foreach (var item in collection.Where(item => item >= 2))
            Console.WriteLine(item);
    }
}

再来举一个例子,两种不同的方式打印数组序列与对应位置值:

class Program
{
    static void Main()
    {
        var collection = new int[] { 1, 2, 2, 3, 4, 6 };
        //方法一:循环遍历
        for (int i = 0; i < collection.Length; i++)
        {
            Console.WriteLine("index:{0}, value:{1}", i, collection[i]);
        }
        
        //方法二:LINQ直接获取并定义最终数据组成的结果格式,再打印出来
        //在 C# LINQ 的 Select方法中,第一个参数是值(value),第二个参数是索引(index) 
        //所以这里(j,k)是直接拿到了数组每个位置的值j和索引号k
        foreach (var result in collection.Select((j, k) => $"index:{k}, value:{j}"))
            Console.WriteLine(result);
    }
}

LINQ 的基本语法

LINQ 提供了三种语法形式

1. 查询表达式语法(Query Syntax)

var result = from item in collection
             where item.Condition
             select item;

var result = from student in students
             where student.Age > 18
             select student.Name;

2. 方法语法(Method Syntax)

var result = collection.Where(item => item.Condition);

var result = students.Where(s => s.Age > 18)
                     .Select(s => s.Name);

3. 混合语法(Mixed Syntax)

var result = (from student in students
              where student.Age > 18
              select student).ToList();

基础案例演示

让我们通过一些简单的例子来理解 LINQ 的使用。

案例1:基本的查询操作

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        // 创建一个整数数组

        int[ ] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        
        // 使用 LINQ 查询表达式找出所有偶数
        var evenNumbers = from num in numbers
                          where num % 2 == 0
                          select num;
        
        Console.WriteLine("偶数:");
        foreach (var num in evenNumbers)
        {
            Console.Write(num + " ");
        }
        // 输出结果:2 4 6 8 10
        
        Console.WriteLine("\n" + new string('-', 30));
        
        // 使用方法语法实现相同功能
        var evenNumbersMethod = numbers.Where(num => num % 2 == 0);
        
        Console.WriteLine("使用方法语法的偶数:");
        foreach (var num in evenNumbersMethod)
        {
            Console.Write(num + " ");
        }
        // 输出结果:2 4 6 8 10
    }
}

数据流向图解

graph LR
    A[原始数组: 1,2,3,4,5,6,7,8,9,10] --> B{筛选条件: num % 2 == 0}
    B --> C[结果数组: 2,4,6,8,10]
    style A fill:#e1f5fe
    style C fill:#c8e6c9
    style B fill:#fff3e0

案例2:字符串集合操作

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        // 创建一个字符串列表
        List<string> names = new List<string> 
        { 
            "Alice", "Bob", "Charlie", "David", "Eve", "Frank" 
        };
        
        // 找出名字长度大于4的人员
        var longNames = from name in names
                        where name.Length > 4
                        select name;
        
        Console.WriteLine("名字长度大于4的人员:");
        foreach (var name in longNames)
        {
            Console.WriteLine(name);
        }
        // 输出结果:
        // Charlie
        // David
        // Frank
        
        Console.WriteLine(new string('-', 30));
        
        // 按字母顺序排序
        var sortedNames = names.OrderBy(name => name);
        
        Console.WriteLine("按字母顺序排序:");
        foreach (var name in sortedNames)
        {
            Console.WriteLine(name);
        }
        // 输出结果:
        // Alice
        // Bob
        // Charlie
        // David
        // Eve
        // Frank
    }
}

排序流程图解

graph TB
    A[原始列表: Alice,Bob,Charlie,David,Eve,Frank] --> B[按字母排序]
    B --> C[排序后: Alice,Bob,Charlie,David,Eve,Frank]
    style A fill:#e1f5fe
    style C fill:#c8e6c9
    style B fill:#fff3e0

LINQ 常用操作符详解

1. 筛选操作符

Where - 条件筛选

Where 操作符用于根据指定条件筛选集合中的元素。

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        // 创建学生集合
        var students = new List<Student>
        {
            new Student { Id = 1, Name = "张三", Age = 20, Score = 85 },
            new Student { Id = 2, Name = "李四", Age = 17, Score = 92 },
            new Student { Id = 3, Name = "王五", Age = 22, Score = 78 },
            new Student { Id = 4, Name = "赵六", Age = 19, Score = 88 }
        };

        // 筛选年龄大于18岁的学生
        var adultStudents = students.Where(s => s.Age > 18);

        Console.WriteLine("年龄大于18岁的学生:");
        foreach (var student in adultStudents)
        {
            Console.WriteLine($"姓名:{student.Name},年龄:{student.Age}");
        }
        // 输出结果:
        // 姓名:张三,年龄:20
        // 姓名:王五,年龄:22
        // 姓名:赵六,年龄:19
    }
}

class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public int Score { get; set; }
}
graph LR
    A[原始集合] --> B{条件判断}
    B -->|满足条件| C[结果集合]
    B -->|不满足条件| D[过滤掉]
    
    subgraph 条件判断
        B
    end
    
    subgraph 结果
        C
    end

2. 排序操作符

OrderBy - 升序排序

using System;
using System.Linq;

class Program
{
    static void Main()
    {

        int[ ] numbers = { 5, 2, 8, 1, 9, 3 };

        
        // 升序排序
        var ascending = numbers.OrderBy(x => x);
        
        Console.WriteLine("升序排序:");
        foreach (var num in ascending)
        {
            Console.Write(num + " ");
        }
        // 输出结果:1 2 3 5 8 9
    }
}

OrderByDescending - 降序排序

using System;
using System.Linq;

class Program
{
    static void Main()
    {

        int[ ] numbers = { 5, 2, 8, 1, 9, 3 };

        
        // 降序排序
        var descending = numbers.OrderByDescending(x => x);
        
        Console.WriteLine("降序排序:");
        foreach (var num in descending)
        {
            Console.Write(num + " ");
        }
        // 输出结果:9 8 5 3 2 1
    }
}

3. 投影操作符

Select - 数据转换(投影转换)

Select 操作符用于从每个元素中选择特定的属性或创建新的对象。

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        var students = new List<Student>
        {
            new Student { Id = 1, Name = "张三", Age = 20, Score = 85 },
            new Student { Id = 2, Name = "李四", Age = 17, Score = 92 },
            new Student { Id = 3, Name = "王五", Age = 22, Score = 78 }
        };

        // 只选择学生的姓名
        var names = students.Select(s => s.Name);
        Console.WriteLine("学生姓名列表:");
        foreach (var name in names)
        {
            Console.WriteLine(name);
        }
        // 输出结果:
        // 张三
        // 李四
        // 王五

        Console.WriteLine();

        // 创建新的匿名对象
        var studentInfo = students.Select(s => new { 
            姓名 = s.Name, 
            成绩等级 = s.Score >= 90 ? "优秀" : "良好" 
        });
        
        Console.WriteLine("学生成绩信息:");
        foreach (var info in studentInfo)
        {
            Console.WriteLine($"姓名:{info.姓名},成绩等级:{info.成绩等级}");
        }
        // 输出结果:
        // 姓名:张三,成绩等级:良好
        // 姓名:李四,成绩等级:优秀
        // 姓名:王五,成绩等级:良好
    }
}

class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public int Score { get; set; }
}
graph LR
    A[原始集合] --> B[投影转换]
    B --> C[新集合]
    
    subgraph 转换过程
        B
    end
    
    subgraph 结果
        C
    end

4. GroupBy - 分组操作

GroupBy 操作符用于将集合中的元素按照指定的键进行分组。

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        var students = new List<Student>
        {
            new Student { Id = 1, Name = "张三", Age = 20, Score = 85, Class = "A班" },
            new Student { Id = 2, Name = "李四", Age = 17, Score = 92, Class = "A班" },
            new Student { Id = 3, Name = "王五", Age = 22, Score = 78, Class = "B班" },
            new Student { Id = 4, Name = "赵六", Age = 19, Score = 88, Class = "B班" },
            new Student { Id = 5, Name = "钱七", Age = 21, Score = 95, Class = "A班" }
        };

        // 按班级分组
        var groupedByClass = students.GroupBy(s => s.Class);
        
        Console.WriteLine("按班级分组:");
        foreach (var group in groupedByClass)
        {
            Console.WriteLine($"班级:{group.Key}");
            foreach (var student in group)
            {
                Console.WriteLine($"  - {student.Name}(成绩:{student.Score})");
            }
        }
        // 输出结果:
        // 班级:A班
        //   - 张三(成绩:85)
        //   - 李四(成绩:92)
        //   - 钱七(成绩:95)
        // 班级:B班
        //   - 王五(成绩:78)
        //   - 赵六(成绩:88)
    }
}

class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public int Score { get; set; }
    public string Class { get; set; }
}
graph LR
    A[原始集合] --> B[分组键计算]
    B --> C{分组}
    C -->|A班| D[A班组]
    C -->|B班| E[B班组]
    
    subgraph 分组过程
        B
        C
    end
    
    subgraph 结果
        D
        E
    end

5. Join - 连接操作

Join 操作符用于将两个集合基于共同的键进行连接。

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        // 学生信息
        var students = new List<Student>
        {
            new Student { Id = 1, Name = "张三", ClassId = 1 },
            new Student { Id = 2, Name = "李四", ClassId = 2 },
            new Student { Id = 3, Name = "王五", ClassId = 1 }
        };

        // 班级信息
        var classes = new List<Class>
        {
            new Class { Id = 1, ClassName = "计算机科学与技术" },
            new Class { Id = 2, ClassName = "软件工程" }
        };

        // 内连接:获取学生及其班级信息
        var studentWithClass = from student in students
                              join cls in classes on student.ClassId equals cls.Id
                              select new
                              {
                                  StudentName = student.Name,
                                  ClassName = cls.ClassName
                              };

        Console.WriteLine("学生班级信息:");
        foreach (var item in studentWithClass)
        {
            Console.WriteLine($"{item.StudentName} - {item.ClassName}");
        }
        // 输出结果:
        // 张三 - 计算机科学与技术
        // 李四 - 软件工程
        // 王五 - 计算机科学与技术
    }
}

class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int ClassId { get; set; }
}

class Class
{
    public int Id { get; set; }
    public string ClassName { get; set; }
}
graph LR
    A[学生集合] --> C{连接条件}
    B[班级集合] --> C
    C --> D[匹配结果]
    
    subgraph 连接过程
        C
    end
    
    subgraph 结果
        D
    end

6. 聚合操作符

Count - 计数

using System;
using System.Linq;

class Program
{
    static void Main()
    {

        int[ ] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        
        // 统计偶数的个数
        var evenCount = numbers.Count(x => x % 2 == 0);
        
        Console.WriteLine($"偶数个数:{evenCount}");
        // 输出结果:偶数个数:5
    }
}

Sum - 求和

using System;
using System.Linq;

class Program
{
    static void Main()
    {

        int[ ] numbers = { 1, 2, 3, 4, 5 };

        
        // 计算总和
        var sum = numbers.Sum();
        
        Console.WriteLine($"总和:{sum}");
        // 输出结果:总和:15
    }
}

Max/Min - 最大值/最小值

using System;
using System.Linq;

class Program
{
    static void Main()
    {

        int[ ] numbers = { 3, 1, 4, 1, 5, 9, 2, 6 };

        
        // 找出最大值和最小值
        var max = numbers.Max();
        var min = numbers.Min();
        
        Console.WriteLine($"最大值:{max}");  // 输出:最大值:9
        Console.WriteLine($"最小值:{min}");  // 输出:最小值:1
    }
}

复杂案例:学生管理系统

让我们通过一个更复杂的例子来展示 LINQ 的强大功能。

using System;
using System.Collections.Generic;
using System.Linq;

// 学生类
public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public double Score { get; set; }
    public string Major { get; set; }
}

class Program
{
    static void Main()
    {
        // 创建学生列表
        List<Student> students = new List<Student>
        {
            new Student { Id = 1, Name = "张三", Age = 20, Score = 85.5, Major = "计算机" },
            new Student { Id = 2, Name = "李四", Age = 21, Score = 92.0, Major = "数学" },
            new Student { Id = 3, Name = "王五", Age = 19, Score = 78.5, Major = "计算机" },
            new Student { Id = 4, Name = "赵六", Age = 22, Score = 88.0, Major = "物理" },
            new Student { Id = 5, Name = "钱七", Age = 20, Score = 95.5, Major = "计算机" }
        };
        
        Console.WriteLine("=== 所有学生信息 ===");
        foreach (var student in students)
        {
            Console.WriteLine($"ID:{student.Id}, 姓名:{student.Name}, 年龄:{student.Age}, 成绩:{student.Score}, 专业:{student.Major}");
        }
        
        Console.WriteLine("\n=== LINQ 查询演示 ===");
        
        // 1. 找出成绩大于85分的学生
        var highScoreStudents = students.Where(s => s.Score > 85);
        Console.WriteLine("\n成绩大于85分的学生:");
        foreach (var student in highScoreStudents)
        {
            Console.WriteLine($"{student.Name}: {student.Score}");
        }
        // 输出结果:
        // 李四: 92
        // 赵六: 88
        // 钱七: 95.5
        
        // 2. 按成绩降序排列
        var sortedByScore = students.OrderByDescending(s => s.Score);
        Console.WriteLine("\n按成绩降序排列:");
        foreach (var student in sortedByScore)
        {
            Console.WriteLine($"{student.Name}: {student.Score}");
        }
        // 输出结果:
        // 钱七: 95.5
        // 李四: 92
        // 赵六: 88
        // 张三: 85.5
        // 王五: 78.5
        
        // 3. 统计计算机专业的学生
        var csStudents = students.Where(s => s.Major == "计算机");
        Console.WriteLine("\n计算机专业的学生:");
        foreach (var student in csStudents)
        {
            Console.WriteLine($"{student.Name}");
        }
        // 输出结果:
        // 张三
        // 王五
        // 钱七
        
        // 4. 计算平均成绩
        var averageScore = students.Average(s => s.Score);
        Console.WriteLine($"\n平均成绩:{averageScore:F2}");
        // 输出结果:平均成绩:87.90
        
        // 5. 分组查询:按专业分组
        var groupedByMajor = students.GroupBy(s => s.Major);
        Console.WriteLine("\n按专业分组:");
        foreach (var group in groupedByMajor)
        {
            Console.WriteLine($"{group.Key}专业:");
            foreach (var student in group)
            {
                Console.WriteLine($"  {student.Name}");
            }
        }
        // 输出结果:
        // 计算机专业:
        //   张三
        //   王五
        //   钱七
        // 数学专业:
        //   李四
        // 物理专业:
        //   赵六
    }
}

数据处理流程图

graph TB
    A[学生数据集合] --> B[Where筛选]
    B --> C[OrderBy排序]
    C --> D[GroupBy分组]
    D --> E[聚合计算]
    style A fill:#e1f5fe
    style B fill:#fff3e0
    style C fill:#fff3e0
    style D fill:#fff3e0
    style E fill:#c8e6c9

高级查询示例

static void AdvancedQueryExamples(List<Student> students)
{
    Console.WriteLine("=== 高级查询示例 ===\n");

    // 1. 复合筛选条件
    Console.WriteLine("1. 筛选计算机科学专业且成绩大于80分的学生:");
    var csTopStudents = students.Where(s => s.Major == "计算机科学" && s.Score > 80);
    foreach (var student in csTopStudents)
    {
        Console.WriteLine($"   {student.Name} - {student.Major} - {student.Score}分");
    }
    // 输出结果:
    // 张三 - 计算机科学 - 85.5分

    Console.WriteLine();

    // 2. 分组查询 - 按专业分组统计学生数量
    Console.WriteLine("2. 按专业分组统计学生数量:");
    var groupByMajor = students.GroupBy(s => s.Major);
    foreach (var group in groupByMajor)
    {
        Console.WriteLine($"   {group.Key}专业:{group.Count()}人");
        foreach (var student in group)
        {
            Console.WriteLine($"     - {student.Name}");
        }
    }
    // 输出结果:
    // 计算机科学专业:2人
    //   - 张三
    //   - 王五
    // 数学专业:2人
    //   - 李四
    //   - 钱七
    // 物理专业:1人
    //   - 赵六

    Console.WriteLine();

    // 3. 聚合函数 - 计算平均分
    Console.WriteLine("3. 各专业平均分:");
    var avgScoreByMajor = students.GroupBy(s => s.Major)
                                  .Select(g => new 
                                  { 
                                      Major = g.Key, 
                                      AverageScore = g.Average(s => s.Score) 
                                  });
    foreach (var item in avgScoreByMajor)
    {
        Console.WriteLine($"   {item.Major}专业平均分:{item.AverageScore:F1}分");
    }
    // 输出结果:
    // 计算机科学专业平均分:82.0分
    // 数学专业平均分:93.8分
    // 物理专业平均分:88.0分

    Console.WriteLine();

    // 4. 连接查询 - 模拟学生和课程的关系
    var courses = new List<Course>
    {
        new Course { Id = 1, Name = "C#编程", StudentId = 1 },
        new Course { Id = 2, Name = "高等数学", StudentId = 1 },
        new Course { Id = 3, Name = "线性代数", StudentId = 2 },
        new Course { Id = 4, Name = "数据结构", StudentId = 3 }
    };

    Console.WriteLine("4. 查询学生及其选修课程:");
    var studentCourses = from student in students
                         join course in courses on student.Id equals course.StudentId into courseGroup
                         select new 
                         { 
                             StudentName = student.Name, 
                             Courses = courseGroup.Select(c => c.Name) 
                         };

    foreach (var item in studentCourses)
    {
        Console.WriteLine($"   {item.StudentName}选修的课程:");
        if (item.Courses.Any())
        {
            foreach (var course in item.Courses)
            {
                Console.WriteLine($"     - {course}");
            }
        }
        else
        {
            Console.WriteLine("     - 无选修课程");
        }
    }
    // 输出结果:
    // 张三选修的课程:
    //   - C#编程
    //   - 高等数学
    // 李四选修的课程:
    //   - 线性代数
    // 王五选修的课程:
    //   - 数据结构
    // 赵六选修的课程:
    //   - 无选修课程
    // 钱七选修的课程:
    //   - 无选修课程
}

// 课程类定义
public class Course
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int StudentId { get; set; }
}

常用 LINQ 方法汇总 更多方法点击前往官网详细了解

筛选和投影方法

方法说明示例
Where筛选满足条件的元素list.Where(x => x > 5)
Select投影转换元素list.Select(x => x * 2)
SelectMany展平嵌套集合list.SelectMany(x => x.Items)

排序方法

方法说明示例
OrderBy升序排序list.OrderBy(x => x.Name)
OrderByDescending降序排序list.OrderByDescending(x => x.Age)
ThenBy二级排序list.OrderBy(x => x.Name).ThenBy(x => x.Age)

分组和聚合方法

方法说明示例
GroupBy按键分组list.GroupBy(x => x.Category)
Count计数list.Count()
Sum求和list.Sum(x => x.Price)
Average平均值list.Average(x => x.Score)
Max/Min最大/最小值list.Max(x => x.Age)

元素操作方法

方法说明示例
First获取第一个元素list.First()
Last获取最后一个元素list.Last()
Single获取唯一元素list.Single(x => x.Id == 1)
ElementAt获取指定位置元素list.ElementAt(2)

LINQ 延迟执行特性

LINQ 的一个重要特性是延迟执行,这意味着查询的定义和实际执行是分离的。查询不会立即执行,而是在实际需要数据时才执行。

延迟执行示例

Console.WriteLine("=== 延迟执行示例 ===");

List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
Console.WriteLine("1. 定义查询时:");
var query = numbers.Where(n => n > 2);  // 此时不会执行筛选
Console.WriteLine("   查询已定义,但未执行");

numbers.Add(6);  // 在查询执行前修改数据源
numbers.Add(7);

Console.WriteLine("2. 执行查询时:");
foreach (var item in query)  // 此时才真正执行筛选
{
    Console.WriteLine($"   {item}");
}
// 输出结果会包含6和7,因为它们在查询定义后被添加
// 输出:
// 3
// 4
// 5
// 6
// 7

延迟执行流程图

sequenceDiagram
    participant C as 客户端代码
    participant L as LINQ查询
    participant D as 数据源
    
    C->>L: 定义查询
    Note over L: 不执行,只保存表达式
    
    C->>D: 修改数据源
    D-->>C: 数据源更新
    
    C->>L: 执行查询
    L->>D: 访问最新数据
    D-->>L: 返回结果
    L-->>C: 返回查询结果

立即执行示例

Console.WriteLine("\n=== 立即执行示例 ===");

List<int> numbers2 = new List<int> { 1, 2, 3, 4, 5 };
Console.WriteLine("1. 使用立即执行方法:");
var count = numbers2.Where(n => n > 2).Count();  // Count()是立即执行方法
Console.WriteLine($"   满足条件的元素数量:{count}");

numbers2.Add(6);
numbers2.Add(7);

Console.WriteLine("2. 再次计算数量:");
var count2 = numbers2.Where(n => n > 2).Count();
Console.WriteLine($"   修改后满足条件的元素数量:{count2}");
// 第一次输出:3(3,4,5)
// 第二次输出:5(3,4,5,6,7)

LINQ执行流程图

graph LR
    A[定义LINQ查询] --> B{是否为延迟执行方法?}
    B -->|是| C[返回IEnumerable]
    B -->|否| D[立即执行并返回结果]
    C --> E[访问查询结果]
    E --> F[实际执行查询]
    F --> G[返回数据]

性能考虑

1. 延迟执行 vs 立即执行

// 延迟执行 - 返回 IEnumerable
var delayed = numbers.Where(x => x > 5);

// 立即执行 - 返回具体结果
var immediate = numbers.Where(x => x > 5).ToList();  // ToList() 立即执行

2. 避免重复查询

// 不好的做法 - 每次都重新执行查询
var expensiveQuery = numbers.Where(x => SomeExpensiveOperation(x));
var count = expensiveQuery.Count();
var list = expensiveQuery.ToList();  // 重复执行了查询

// 好的做法 - 使用 ToList() 缓存结果
var cachedResult = numbers.Where(x => SomeExpensiveOperation(x)).ToList();
var count = cachedResult.Count;
var list = cachedResult;

3. 合理使用 ToList()

// 当需要多次遍历时,使用 ToList() 避免重复查询
var query = students.Where(s => s.Age > 18);
var studentList = query.ToList(); // 执行一次查询

// 后续操作都基于内存中的列表
var count = studentList.Count();
var first = studentList.First();

常见错误和注意事项

1. Null 引用异常

List<string> names = new List<string> { "Alice", null, "Bob" };

// 可能抛出 NullReferenceException
// var result = names.Where(name => name.Length > 3);

// 安全的做法
var result = names.Where(name => name != null && name.Length > 3);

2. 延迟执行的副作用

List<int> numbers = new List<int> { 1, 2, 3 };
var query = numbers.Where(x => x > 1);

numbers.Clear();  // 修改了原始数据

// 此时查询结果为空,因为原始数据被清空了
foreach (var item in query)
{
    Console.WriteLine(item);  // 不会输出任何内容
}