C# 中的 LINQ 语句

LINQ (Language Integrated Query) 是 C# (以及 VB.NET) 中的一组技术,它将查询功能直接集成到 C# 语言中,提供了一种统一的、类型安全的方式来查询和操作来自各种数据源的数据。

简单来说,LINQ 语句允许你使用类似于 SQL 的语法来查询内存中的对象集合 (如 List<T>, Array)、XML 文档、数据库 (通过 LINQ to SQL, Entity Framework 等)、ADO.NET 数据集等。

核心思想

  1. 语言集成 (Language Integrated): 查询不再是作为字符串传递给数据库或其他系统的命令,而是 C# 语言本身的一部分。这意味着你可以获得编译时类型检查、IntelliSense 支持和调试器支持。
  2. 统一的查询模型 (Unified Query Model): 无论数据源是什么(对象、XML、数据库),查询的基本语法和模式都是相似的。这减少了学习不同数据访问 API 的复杂性。
  3. 强类型 (Strongly Typed): 查询的结果通常也是强类型的对象,这有助于在编译时捕获错误,而不是在运行时。

主要语法形式

  1. 查询语法 (Query Syntax / Declarative Syntax):

    • 这种语法非常类似于 SQL,对于熟悉 SQL 的开发者来说更容易理解和上手。
    • 它使用 from, where, select, orderby, join, group by 等关键字。
    • 更具可读性,尤其对于复杂的查询。

    示例 (查询语法):

    // 假设有一个名为 numbers 的整数列表
    List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    
    // 查询所有大于 5 的偶数,并按降序排列
    var evenNumbersGreaterThanFive = from num in numbers
                                     where num > 5 && num % 2 == 0
                                     orderby num descending
                                     select num;
    
    foreach (var n in evenNumbersGreaterThanFive)
    {
        Console.WriteLine(n); // 输出: 10, 8, 6
    }
    
  2. 方法语法 (Method Syntax / Fluent Syntax):

    • 这种语法使用一系列的扩展方法 (Extension Methods),这些方法通常定义在 System.Linq.EnumerableSystem.Linq.Queryable 类中。
    • 每个方法通常对应查询语法中的一个子句(例如 Where(), Select(), OrderBy())。
    • 方法语法可以链式调用,对于简单的查询可能更简洁,并且有些 LINQ 操作符只能通过方法语法使用。
    • Lambda 表达式通常与方法语法一起使用来定义查询的逻辑。

    示例 (方法语法):

    // 假设有一个名为 numbers 的整数列表
    List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    
    // 查询所有大于 5 的偶数,并按降序排列
    var evenNumbersGreaterThanFive = numbers
                                     .Where(num => num > 5 && num % 2 == 0)
                                     .OrderByDescending(num => num)
                                     .Select(num => num); // 在这个简单例子中,Select(num => num) 可以省略
    
    foreach (var n in evenNumbersGreaterThanFive)
    {
        Console.WriteLine(n); // 输出: 10, 8, 6
    }
    

关键组成部分

  • from 子句: 指定数据源以及范围变量 (range variable)。每个 LINQ 查询都以 from 子句开始(或者 join 子句引入新的数据源)。
    • from item in collection
  • where 子句 (可选): 用于根据条件筛选数据源中的元素。
    • where item.Property > value
  • select 子句: 指定查询结果中每个元素的形状或类型。它可以返回原始元素、元素的一部分,或者基于原始元素创建新的对象。
    • select item (返回整个元素)
    • select item.Property (返回元素的某个属性)
    • select new { Name = item.Name, Age = item.Age } (返回匿名类型)
  • orderby 子句 (可选): 用于对查询结果进行排序(升序 ascending (默认) 或降序 descending)。
    • orderby item.Property ascending
  • join 子句 (可选): 用于根据键将来自不同数据源的元素关联起来。
    • join otherItem in otherCollection on item.Key equals otherItem.Key
  • group by 子句 (可选): 用于根据指定的键对元素进行分组。
    • group item by item.Category
  • into 关键字: 可以用于在 joingroup by 子句之后创建一个临时的标识符,或者用于在查询末尾继续查询 (查询延续)。

不同提供程序 (Providers)

LINQ 的强大之处在于它可以应用于多种数据源,这是通过不同的 LINQ 提供程序实现的:

  • LINQ to Objects: 用于查询内存中的 IEnumerableIEnumerable<T> 集合 (如数组、列表)。
  • LINQ to XML (XLINQ): 用于查询和操作 XML 文档。
  • LINQ to SQL (已基本被 Entity Framework 取代): 用于查询 Microsoft SQL Server 数据库。它将 LINQ 查询转换为 SQL 语句。
  • LINQ to Entities (Entity Framework): Entity Framework (EF) 的一部分,是微软推荐的 ORM (Object-Relational Mapper),允许你使用 LINQ 查询多种数据库 (SQL Server, MySQL, PostgreSQL 等)。EF Core 是其现代、跨平台的版本。
  • LINQ to DataSets: 用于查询 ADO.NET DataSet 对象。
  • 第三方 LINQ 提供程序: 许多其他数据源(如 NoSQL 数据库、Web 服务)也有自己的 LINQ 提供程序。

优点

  1. 可读性和可维护性: LINQ 查询通常比传统的循环和条件语句更容易阅读和理解,尤其对于复杂的数据操作。
  2. 编译时检查: 由于查询是语言的一部分,编译器可以在编译时捕获语法错误和类型不匹配,减少运行时错误。
  3. IntelliSense 支持: Visual Studio 等 IDE 为 LINQ 查询提供强大的 IntelliSense 支持,包括自动完成和参数信息。
  4. 减少代码量: 对于许多常见的数据操作,LINQ 可以用更少的代码实现相同的功能。
  5. 延迟执行 (Deferred Execution): 许多 LINQ 查询(尤其是返回 IEnumerable<T> 的查询)并不会立即执行,而是在枚举结果时(例如在 foreach 循环中或调用 ToList(), ToArray() 等方法时)才执行。这可以提高性能,尤其是在处理大型数据集或构建动态查询时。某些操作符如 Count(), First() 会立即执行。

总而言之,LINQ 语句是 C# 中一种强大且富有表现力的方式,用于以声明式、类型安全的方式查询和转换数据,无论数据源是什么。它是现代 C# 开发中不可或缺的一部分。