前置知识: C#

LINQ深度解析

00:00
2 min Intermediate 2026/6/14

LINQ查询语法与方法语法

概述

LINQ(Language Integrated Query)是 C# 的特性之一,提供统一的查询语法来操作各种数据。LINQ 支持方法语法和查询语法两种风格,配合 Lambda 表达式可以实现简洁而强大的数据转换和过滤。

基础概念

两种语法风格

// 方法语法(更常用,推荐)
var result = users
    .Where(u => u.Age > 18)
    .OrderBy(u => u.Name)
    .Select(u => new { u.Name, u.Age });

// 查询语法(类似 SQL,适合复杂 join)
var result2 = from u in users
              where u.Age > 18
              orderby u.Name
              select new { u.Name, u.Age };

常用操作符

操作符说明类型
Where过滤延迟
Select投影延迟
OrderBy排序延迟
GroupBy分组延迟
Join连接延迟
GroupJoin分组连接延迟
Distinct去重延迟
Aggregate自定义聚合立即
Zip合并两个序列延迟
ToList转为列表立即
First取第一个立即
Count计数立即

快速上手

过滤与投影

// 过滤
var adults = users.Where(u => u.Age >= 18);
var beijingUsers = users.Where(u => u.City == "北京");

// 多条件过滤
var result = users.Where(u => u.Age > 18 && u.IsActive);

// 投影(转换数据)
var names = users.Select(u => u.Name);
var dtos = users.Select(u => new UserDTO { Name = u.Name, Email = u.Email });

// 展平投影
var allOrders = users.SelectMany(u => u.Orders);
var allTags = articles.SelectMany(a => a.Tags).Distinct();

排序与分组

// 排序
var sorted = users.OrderBy(u => u.Age);              // 升序
var sorted2 = users.OrderByDescending(u => u.Age);   // 降序
var sorted3 = users.OrderBy(u => u.Age).ThenBy(u => u.Name); // 多级排序

// 分组
var groups = users.GroupBy(u => u.City);
foreach (var group in groups) {
    Console.WriteLine($"{group.Key}: {group.Count()} 人");
}

// 分组后投影
var stats = users.GroupBy(u => u.City)
    .Select(g => new {
        City = g.Key,
        Count = g.Count(),
        AvgAge = g.Average(u => u.Age)
    });

详细用法

Join 操作

// 内连接
var result = from u in users
             join o in orders on u.Id equals o.UserId
             select new { u.Name, o.Amount };

// 方法语法
var result2 = users.Join(orders,
    u => u.Id,
    o => o.UserId,
    (u, o) => new { u.Name, o.Amount });

// 左连接
var leftJoin = from u in users
               join o in orders on u.Id equals o.UserId into userOrders
               from o in userOrders.DefaultIfEmpty()
               select new { u.Name, Amount = o?.Amount ?? 0 };

// 分组连接
var groupJoin = users.GroupJoin(orders,
    u => u.Id,
    o => o.UserId,
    (u, os) => new { u.Name, Orders = os });

聚合操作

// 基本聚合
var count = users.Count();
var totalAge = users.Sum(u => u.Age);
var avgAge = users.Average(u => u.Age);
var maxAge = users.Max(u => u.Age);
var minAge = users.Min(u => u.Age);

// 条件计数
var activeCount = users.Count(u => u.IsActive);

// 自定义聚合
var summary = orders.Aggregate(
    new { Total = 0m, Count = 0 },
    (acc, o) => new { Total = acc.Total + o.Amount, Count = acc.Count + 1 },
    acc => new { acc.Total, acc.Count, Average = acc.Total / acc.Count });

// 字符串聚合
var names = users.Select(u => u.Name).Aggregate((a, b) => $"{a}, {b}");

集合操作

// 去重
var uniqueCities = users.Select(u => u.City).Distinct();

// 按属性去重
var uniqueUsers = users.DistinctBy(u => u.Email);

// 集合运算
var union = list1.Union(list2);       // 并集(去重)
var concat = list1.Concat(list2);     // 连接(不去重)
var intersect = list1.Intersect(list2); // 交集
var except = list1.Except(list2);     // 差集

// 分页
var page = users.OrderBy(u => u.Id).Skip((pageNum - 1) * pageSize).Take(pageSize);

常见场景

数据转换管道

// 复杂的数据转换管道
var report = orders
    .Where(o => o.CreatedAt >= startDate && o.CreatedAt <= endDate)
    .GroupBy(o => o.Product.Category)
    .Select(g => new CategoryReport {
        Category = g.Key,
        TotalRevenue = g.Sum(o => o.Amount),
        OrderCount = g.Count(),
        TopProduct = g.GroupBy(o => o.Product.Name)
            .OrderByDescending(pg => pg.Count())
            .First()
            .Key
    })
    .OrderByDescending(r => r.TotalRevenue)
    .ToList();

字典和查找表

// 转为字典
var userDict = users.ToDictionary(u => u.Id, u => u);

// 分组查找表
var usersByCity = users.ToLookup(u => u.City);
var beijingUsers = usersByCity["北京"]; // 获取北京的所有用户

// 带冲突处理的字典
var dict = users.ToDictionary(
    u => u.Email,
    u => u,
    StringComparer.OrdinalIgnoreCase);

注意事项

  • 延迟查询每次枚举都会重新计算,需要缓存时使用 ToList/ToArray
  • SelectMany 用于展平嵌套集合,是 LINQ 中最强大的操作符之一
  • Join 操作在内存集合中使用,数据库查询应使用导航属性
  • 分组操作 GroupBy 返回的是 IGrouping<Key, Element> 序列
  • 使用 Any() 替代 Count() > 0 判断是否存在元素,性能更好
  • 复杂查询建议使用查询语法,更易读

进阶用法

自定义 LINQ 操作符

// 扩展方法实现自定义操作符
public static IEnumerable<TSource> WhereIf<TSource>(
    this IEnumerable<TSource> source,
    bool condition,
    Func<TSource, bool> predicate) {
    return condition ? source.Where(predicate) : source;
}

// 使用:条件过滤
var result = users
    .WhereIf(!string.IsNullOrEmpty(nameFilter), u => u.Name.Contains(nameFilter))
    .WhereIf(minAge.HasValue, u => u.Age >= minAge.Value);

PLINQ 并行查询

// 使用 PLINQ 并行处理大数据集
var result = largeData.AsParallel()
    .Where(item => item.IsValid)
    .Select(item => Transform(item))
    .OrderBy(item => item.Priority)
    .ToList();

// 控制并行度
var result2 = largeData.AsParallel()
    .WithDegreeOfParallelism(4)
    .WithCancellation(cancellationToken)
    .Select(HeavyComputation)
    .ToList();

知识检测

学习进度

-- 已学文档
--% 知识覆盖率

学习推荐

专注模式