C# LINQ
本章将介绍 LINQ(Language Integrated Query),它是一组语言的特性,允许用 C# 语法直接查询各种数据源。
LINQ 概述
什么是 LINQ?
LINQ 是 C# 语言的一部分,提供了从多种数据源(集合、数据库、XML 等)查询数据的统一语法。
// LINQ 查询语法
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// 查询表达式
var result = from n in numbers
where n % 2 == 0
select n * 2;
// 方法语法
var result2 = numbers
.Where(n => n % 2 == 0)
.Select(n => n * 2);
LINQ 组成
| 组件 | 说明 |
|---|---|
| LINQ to Objects | 内存中的集合 |
| LINQ to XML | XML 文档 |
| LINQ to Entities | Entity Framework |
| LINQ to SQL | SQL Server |
基本查询操作
Where - 过滤
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// 方法语法
var evens = numbers.Where(n => n % 2 == 0);
// 查询语法
var evens2 = from n in numbers
where n % 2 == 0
select n;
Console.WriteLine(string.Join(", ", evens)); // 2, 4, 6, 8, 10
Select - 投影
int[] numbers = { 1, 2, 3, 4, 5 };
// 转换
var squares = numbers.Select(n => n * n);
// 选择特定属性
class Person { public string Name { get; set; } public int Age { get; set; } }
Person[] people = { new Person { Name = "张三", Age = 20 } };
var names = people.Select(p => p.Name);
// 使用 SelectMany 展开嵌套集合
List<string>[] nested = new[] { new List<string> { "a", "b" }, new List<string> { "c", "d" } };
var flat = nested.SelectMany(list => list);
Select + Where 组合
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// 链式调用
var result = numbers
.Where(n => n > 3) // 先过滤
.Select(n => n * 2) // 再投影
.Where(n => n < 20); // 继续过滤
使用 Let 关键字(查询语法)
int[] numbers = { 1, 2, 3, 4, 5, 6 };
var result = from n in numbers
let square = n * n
where square > 10
select new { Original = n, Square = square };
排序操作
OrderBy / OrderByDescending
class Student
{
public string Name { get; set; }
public int Score { get; set; }
}
Student[] students = {
new Student { Name = "张三", Score = 85 },
new Student { Name = "李四", Score = 92 },
new Student { Name = "王五", Score = 78 }
};
// 升序
var byScore = students.OrderBy(s => s.Score);
// 降序
var byScoreDesc = students.OrderByDescending(s => s.Score);
// 按多个字段排序
var multiOrder = students
.OrderBy(s => s.Score)
.ThenBy(s => s.Name);
Reverse
int[] numbers = { 1, 2, 3, 4, 5 };
var reversed = numbers.Reverse(); // 5, 4, 3, 2, 1
分组操作
GroupBy
class Product
{
public string Category { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
Product[] products = {
new Product { Category = "食品", Name = "苹果", Price = 5 },
new Product { Category = "食品", Name = "香蕉", Price = 3 },
new Product { Category = "饮料", Name = "可乐", Price = 3 },
new Product { Category = "饮料", Name = "果汁", Price = 5 }
};
// 按类别分组
var groups = products.GroupBy(p => p.Category);
foreach (var group in groups)
{
Console.WriteLine($"类别: {group.Key}");
foreach (var p in group)
{
Console.WriteLine($" - {p.Name}: {p.Price}");
}
}
按多个字段分组
var groups = products.GroupBy(p => new { p.Category, p.Price > 5 ? "贵" : "便宜" });
GroupBy + 聚合
var categoryStats = products
.GroupBy(p => p.Category)
.Select(g => new
{
Category = g.Key,
Count = g.Count(),
TotalValue = g.Sum(p => p.Price),
AvgPrice = g.Average(p => p.Price)
});
联接操作
Join
class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
public decimal Total { get; set; }
}
class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}
Order[] orders = { new Order { Id = 1, CustomerId = 1, Total = 100 } };
Customer[] customers = { new Customer { Id = 1, Name = "张三" } };
// 联接查询
var result = from o in orders
join c in customers on o.CustomerId equals c.Id
select new { OrderId = o.Id, CustomerName = c.Name, Total = o.Total };
多表联接
var result = from o in orders
join c in customers on o.CustomerId equals c.Id
join p in products on o.ProductId equals p.Id
select new { Order = o, Customer = c, Product = p };
集合操作
集合运算符
int[] a = { 1, 2, 3, 4 };
int[] b = { 3, 4, 5, 6 };
// 并集
var union = a.Union(b); // {1,2,3,4,5,6}
// 交集
var intersect = a.Intersect(b); // {3,4}
// 差集
var except = a.Except(b); // {1,2}
// 去重
var distinct = a.Distinct(); // {1,2,3,4,5,6}
集合判断
int[] numbers = { 1, 2, 3, 4, 5 };
// 判断是否有元素
bool any = numbers.Any(); // true
bool anyEven = numbers.Any(n => n % 2 == 0); // true
// 判断是否所有元素都满足
bool allPositive = numbers.All(n => n > 0); // true
// 判断是否包含
bool contains = numbers.Contains(3); // true
元素操作
First / Last
int[] numbers = { 1, 2, 3, 4, 5 };
var first = numbers.First(); // 1
var firstEven = numbers.First(n => n % 2 == 0); // 2
var last = numbers.Last(); // 5
// 找不到时返回默认值
var firstOrDefault = numbers.FirstOrDefault(); // 1
var firstOrDefault2 = numbers.FirstOrDefault(n => n > 10); // 0
ElementAt / Single
int[] numbers = { 1, 2, 3, 4, 5 };
var element = numbers.ElementAt(2); // 3
// Single:返回唯一元素(没有或多个会抛异常)
int[] single = { 5 };
var singleElement = single.Single(); // 5
聚合操作
Count / Sum / Average
int[] numbers = { 1, 2, 3, 4, 5 };
int count = numbers.Count(); // 5
int sum = numbers.Sum(); // 15
double avg = numbers.Average(); // 3
int max = numbers.Max(); // 5
int min = numbers.Min(); // 1
Aggregate - 聚合
int[] numbers = { 1, 2, 3, 4, 5 };
// 累加器
int product = numbers.Aggregate((acc, n) => acc * n); // 120 (1*2*3*4*5)
// 带初始值
int sum = numbers.Aggregate(100, (acc, n) => acc + n); // 115 (100+1+2+3+4+5)
分区操作
int[] numbers = { 1, 2, 3, 4, 5, 6 };
// Take:取前几个
var take3 = numbers.Take(3); // {1,2,3}
// Skip:跳过前几个
var skip2 = numbers.Skip(2); // {3,4,5,6}
// TakeWhile:满足条件时取
var takeWhile = numbers.TakeWhile(n => n < 4); // {1,2,3}
// SkipWhile:跳过满足条件的
var skipWhile = numbers.SkipWhile(n => n < 4); // {4,5,6}
转换操作
ToArray / ToList / ToDictionary
var list = new List<int> { 1, 2, 3 };
int[] array = list.ToArray(); // 转换为数组
List<int> newList = array.ToList(); // 转换为列表
// 转换为字典
class Product { public string Code { get; set; } public string Name { get; set; } }
var products = new Product[] { new Product { Code = "A01", Name = "苹果" } };
Dictionary<string, string> dict = products.ToDictionary(p => p.Code, p => p.Name);
ToLookup
// 类似 GroupBy,返回 ILookup
var lookup = products.ToLookup(p => p.Category);
foreach (var group in lookup)
{
Console.WriteLine(group.Key);
foreach (var p in group)
Console.WriteLine($" {p.Name}");
}
延迟执行
延迟 vs 立即执行
int[] numbers = { 1, 2, 3, 4, 5 };
// 延迟执行:查询不会立即执行
var query = numbers.Where(n => n > 2).Select(n => n * 2);
// 修改原集合
numbers[0] = 100;
// 真正执行时才会求值
foreach (var n in query)
Console.WriteLine(n); // 输出: 6, 8, 10(100被过滤掉了)
立即执行
// ToList() 等操作会立即执行
var list = numbers.Where(n => n > 2).ToList();
// Count 也会立即执行
int count = numbers.Count(n => n > 2);
小结
- 查询方法:Where、Select、OrderBy、GroupBy
- 联接操作:Join、GroupJoin
- 集合操作:Union、Intersect、Except
- 聚合操作:Sum、Average、Max、Min、Count
- 延迟执行:LINQ 查询是延迟执行的
- 转换操作:ToArray、ToList、ToDictionary
练习
- 使用 LINQ 实现学生成绩统计(平均分、最高分)
- 实现商品分类统计(每类商品数量、总价值)
- 使用 LINQ 实现排行榜功能(排序、分页)
- 实现多表联接查询(订单、客户、商品)