跳到主要内容

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 XMLXML 文档
LINQ to EntitiesEntity Framework
LINQ to SQLSQL 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);

小结

  1. 查询方法:Where、Select、OrderBy、GroupBy
  2. 联接操作:Join、GroupJoin
  3. 集合操作:Union、Intersect、Except
  4. 聚合操作:Sum、Average、Max、Min、Count
  5. 延迟执行:LINQ 查询是延迟执行的
  6. 转换操作:ToArray、ToList、ToDictionary

练习

  1. 使用 LINQ 实现学生成绩统计(平均分、最高分)
  2. 实现商品分类统计(每类商品数量、总价值)
  3. 使用 LINQ 实现排行榜功能(排序、分页)
  4. 实现多表联接查询(订单、客户、商品)