它是什么
LINQ(Language Integrated Query)= C# 提供的集合查询语法。对任何实现 IEnumerable<T> 的东西都能用:
var nums = new[] { 1, 2, 3, 4, 5 };
var evens = nums.Where(n => n % 2 == 0).Select(n => n * n);
foreach (var n in evens) Console.WriteLine(n);
// 4, 16
Where 过滤、Select 映射。其余几十个方法。
链式 vs 查询语法
// 链式(方法)
var result = nums
.Where(n => n > 2)
.OrderBy(n => n)
.Select(n => n * 10)
.ToList();
// 查询语法(SQL-like)
var result2 = (from n in nums
where n > 2
orderby n
select n * 10).ToList();
两者完全等价——查询语法编译时翻译成方法链。新代码用链式,可读 + 全功能(不是所有方法都有查询语法对应)。
最常用的方法
// 过滤
nums.Where(n => n > 2)
// 映射
nums.Select(n => n * 2)
users.Select(u => u.Name)
// 排序
nums.OrderBy(n => n)
nums.OrderByDescending(n => n)
users.OrderBy(u => u.Age).ThenBy(u => u.Name)
// 取部分
nums.Take(3) // 前 3
nums.Skip(2) // 跳前 2
nums.Take(2..5) // 索引 2..5(Take(Range) 自 .NET 6 起)
// 单个值
nums.First() // 第一个;空抛错
nums.FirstOrDefault() // 第一个;空返 default
nums.Single() // 必须正好一个
nums.SingleOrDefault() // 0 或 1 个
nums.Last() / .LastOrDefault()
// 聚合
nums.Count()
nums.Sum()
nums.Average()
nums.Max() / .Min()
nums.Any() // 至少一个
nums.Any(n => n > 100) // 至少一个 > 100
nums.All(n => n > 0) // 全部 > 0
// 集合操作
nums.Distinct()
nums.Union(other)
nums.Intersect(other)
nums.Except(other)
nums.Contains(3)
// 分组
users.GroupBy(u => u.Department)
.Select(g => new { Dep = g.Key, Count = g.Count() })
// 连接(类似 SQL JOIN)
users.Join(orders, u => u.Id, o => o.UserId, (u, o) => new { u.Name, o.Total })
物化(执行)
LINQ 是延迟执行——Where / Select 只是描述,不真的算:
var q = nums.Where(n => { Console.WriteLine("check " + n); return n > 2; });
// 这里不打印
foreach (var x in q) Console.WriteLine(x);
// 现在才 check 1, check 2, check 3, 3, check 4, 4, ...
强制立即执行:
.ToList() // → List<T>
.ToArray() // → T[]
.ToDictionary(...)// → Dictionary
.ToHashSet() // → HashSet
.ToLookup(...) // → ILookup(一对多)
.Count() // 也是立即(要遍历整个序列)
.First() // 立即
坑:链上反复 .Count() 反复遍历——记得 ToList 一次再用。
GroupBy 例
var orders = new[] {
new { User = "Alice", Amount = 100 },
new { User = "Bob", Amount = 50 },
new { User = "Alice", Amount = 200 },
};
var byUser = orders
.GroupBy(o => o.User)
.Select(g => new {
User = g.Key,
Total = g.Sum(o => o.Amount),
Count = g.Count(),
});
foreach (var g in byUser)
Console.WriteLine($"{g.User}: {g.Count} 单, 共 {g.Total}");
// Alice: 2 单, 共 300
// Bob: 1 单, 共 50
内连接
var users = new[] {
new { Id = 1, Name = "Alice" },
new { Id = 2, Name = "Bob" },
};
var orders = new[] {
new { UserId = 1, Total = 100 },
new { UserId = 1, Total = 200 },
};
var joined = users.Join(
orders,
u => u.Id, // user 的 key
o => o.UserId, // order 的 key
(u, o) => new { u.Name, o.Total }
);
左连接 = GroupJoin + SelectMany
var leftJoin = from u in users
join o in orders on u.Id equals o.UserId into ords
from o in ords.DefaultIfEmpty()
select new { u.Name, Total = o?.Total ?? 0 };
链式写法比较啰嗦——左连接是查询语法略胜的少数场景之一。
SelectMany:扁平化
var teachers = new[] {
new { Name = "A", Students = new[] { "Alice", "Bob" } },
new { Name = "B", Students = new[] { "Charlie" } },
};
var allStudents = teachers.SelectMany(t => t.Students);
// "Alice", "Bob", "Charlie"
类似 JS 的 flatMap。
自定义比较器
nums.Distinct(EqualityComparer<int>.Default);
users.OrderBy(u => u.Name, StringComparer.OrdinalIgnoreCase);
users.GroupBy(u => u.Email, StringComparer.OrdinalIgnoreCase);
很多 LINQ 方法接受可选的 IEqualityComparer / IComparer 参数。
LINQ to SQL(EF Core)
// 同样的 LINQ 语法,但翻译成 SQL
var users = db.Users
.Where(u => u.Age >= 18)
.OrderBy(u => u.Name)
.Take(10)
.ToList(); // 这里才真正执行 SQL
EF Core 把 LINQ 表达式树翻译成数据库方言——同一份代码可以查 SQL Server / PostgreSQL / SQLite。
性能
- LINQ 方法通常比手写循环慢 2-5 倍——不是数量级,是常数倍
- 热循环里 1M+ 元素 → 写 for / foreach 可能更快
- 一般业务代码用 LINQ:可读性 >> 微小性能差
// 同样意思
var sum1 = nums.Where(n => n > 0).Sum(); // LINQ
int sum2 = 0;
foreach (var n in nums) if (n > 0) sum2 += n;
实用方法(.NET 6+ 新增)
nums.Chunk(3) // 分块:[1,2,3], [4,5,6], ...
nums.MinBy(x => x.Property) // 最小(带选择器)
nums.MaxBy(x => x.Property)
nums.DistinctBy(x => x.Name)
nums.UnionBy(other, x => x.Id)
nums.Zip(other, (a, b) => ...)
MinBy / MaxBy 特别实用——之前要 OrderBy().First(),现在一行。
→ 下一篇 async / await