C# Lambda 表达式与 LINQ

18.1 Lambda 表达式

基本语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 参数列表 => 表达式
(参数) => 表达式

// 无参数
() => Console.WriteLine("Hello")

// 一个参数
x => x * x

// 多个参数
(x, y) => x + y

// 多行语句
(x, y) =>
{
int result = x + y;
return result;
}

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 使用 Func
Func<int, int> square = x => x * x;
Console.WriteLine(square(5)); // 25

// 使用 Action
Action<string> greet = name => Console.WriteLine($"Hello, {name}");
greet("Alice");

// 多行 lambda
Func<int, int, int> calculate = (a, b) =>
{
int sum = a + b;
int product = a * b;
return sum + product;
};

18.2 LINQ 概述

LINQ(Language Integrated Query)是 C# 中强大的数据查询功能。

数据源

1
2
3
4
5
6
7
8
// 数组
int[] numbers = { 5, 2, 8, 1, 9 };

// 列表
List<string> fruits = new() { "apple", "banana", "cherry" };

// 数据库(Entity Framework)
// var users = db.Users.Where(u => u.Age > 18);

18.3 查询语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
int[] numbers = { 5, 2, 8, 1, 9, 3, 7, 4, 6 };

// 基本查询
var evenNumbers = from n in numbers
where n % 2 == 0
orderby n
select n;

// 输出: 2, 4, 6, 8
foreach (var n in evenNumbers)
{
Console.Write($"{n} ");
}

// 投影
var squares = from n in numbers
where n > 3
select new { Number = n, Square = n * n };

foreach (var item in squares)
{
Console.WriteLine($"{item.Number}² = {item.Square}");
}

// 分组
string[] words = { "apple", "banana", "cherry", "avocado", "blueberry" };

var groups = from w in words
group w by w[0] into g
orderby g.Key
select new { FirstLetter = g.Key, Words = g };

foreach (var group in groups)
{
Console.WriteLine($"{group.FirstLetter}: {string.Join(", ", group.Words)}");
}

18.4 方法语法(推荐)

基本操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
List<int> numbers = new() { 5, 2, 8, 1, 9, 3, 7, 4, 6 };

// Where 过滤
var evens = numbers.Where(n => n % 2 == 0); // 2, 4, 6, 8

// Select 投影
var squared = numbers.Select(n => n * n); // 25, 4, 64, ...

// OrderBy 排序
var sorted = numbers.OrderBy(n => n); // 1, 2, 3, 4, 5, 6, 7, 8, 9
var desc = numbers.OrderByDescending(n => n); // 9, 8, 7, 6, 5, 4, 3, 2, 1

// ThenBy 次级排序
var words = new[] { "apple", "Cat", "banana", "Dog" };
var sortedWords = words.OrderBy(w => w.Length).ThenBy(w => w);

聚合操作

1
2
3
4
5
6
7
8
9
10
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
int first = numbers.First(); // 1
int firstOrDefault = numbers.FirstOrDefault(n => n > 10); // 0
int single = numbers.Single(n => n == 3); // 3

18.5 常用操作符

Filter - Where

1
2
var adults = people.Where(p => p.Age >= 18);
var games = products.Where(p => p.Category == "Game" && p.Price < 100);

Projection - Select

1
2
3
4
5
6
7
8
// 选择单个属性
var names = users.Select(u => u.Name);

// 匿名类型
var infos = users.Select(u => new { u.Name, u.Age });

// 索引
var indexed = words.Select((w, i) => $"{i}: {w}");

Element - First/Last/Single

1
2
3
4
5
6
7
8
9
int[] numbers = { 1, 2, 3, 4, 5 };

int first = numbers.First(); // 1
int last = numbers.Last(); // 5
int single = numbers.Single(n => n == 3); // 3
int firstEven = numbers.First(n => n % 2 == 0); // 2

// 安全的版本
int? maybe = numbers.FirstOrDefault(n => n > 10); // 0

Quantifier - All/Any/Contains

1
2
3
4
5
int[] numbers = { 1, 2, 3, 4, 5 };

bool allPositive = numbers.All(n => n > 0); // True
bool anyEven = numbers.Any(n => n % 2 == 0); // True
bool hasThree = numbers.Contains(3); // True

Set - Distinct/Union/Intersect

1
2
3
4
5
6
7
int[] numbers1 = { 1, 2, 3, 4, 5 };
int[] numbers2 = { 4, 5, 6, 7, 8 };

var distinct = numbers1.Distinct(); // 1, 2, 3, 4, 5
var union = numbers1.Union(numbers2); // 1, 2, 3, 4, 5, 6, 7, 8
var intersect = numbers1.Intersect(numbers2); // 4, 5
var except = numbers1.Except(numbers2); // 1, 2, 3

18.6 LINQ to Objects

实际操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class Student
{
public string Name { get; set; }
public int Score { get; set; }
public string Class { get; set; }
}

List<Student> students = new()
{
new() { Name = "Alice", Score = 85, Class = "A" },
new() { Name = "Bob", Score = 92, Class = "B" },
new() { Name = "Charlie", Score = 78, Class = "A" },
new() { Name = "David", Score = 95, Class = "B" },
};

// 各班平均分
var classAverages = students
.GroupBy(s => s.Class)
.Select(g => new
{
Class = g.Key,
Average = g.Average(s => s.Score),
Count = g.Count()
})
.OrderByDescending(x => x.Average);

foreach (var item in classAverages)
{
Console.WriteLine($"{item.Class}班: 平均{item.Average:F1}分 ({item.Count}人)");
}

// 前 3 名
var top3 = students
.OrderByDescending(s => s.Score)
.Take(3)
.Select((s, i) => $"第{i + 1}名: {s.Name} - {s.Score}分");

foreach (var item in top3)
{
Console.WriteLine(item);
}

18.7 延迟执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int[] numbers = { 1, 2, 3, 4, 5 };

// LINQ 查询不会立即执行
var query = numbers.Where(n =>
{
Console.WriteLine($"检查: {n}");
return n > 2;
});

Console.WriteLine("查询已定义,未执行");

// 遍历时执行
foreach (var n in query)
{
Console.WriteLine($"结果: {n}");
}

// 强制立即执行
var list = query.ToList(); // 立即执行
var array = query.ToArray(); // 立即执行
int count = query.Count(); // 立即执行

18.8 本章小结

  • Lambda 表达式:参数 => 表达式
  • LINQ 有查询语法和方法语法两种形式
  • 方法语法更常用:WhereSelectOrderBy
  • 聚合操作:CountSumAverageMaxMin
  • 分组使用 GroupBy
  • LINQ 查询延迟执行,遍历或强制转换时执行

练习题

  1. 从集合中筛选出所有偶数并排序
  2. 对人员列表按年龄分组
  3. 计算成绩列表的平均分和最高分
  4. 以下代码输出什么?
    1
    2
    3
    int[] nums = { 1, 2, 3, 4, 5 };
    var q = nums.Where(n => n > 3).Select(n => n * n);
    Console.WriteLine(string.Join(",", q));
ByteFisher
分享编程技术 · 记录钓鱼乐趣
扫码关注
▸ 扫码关注 ◂
C#基础教程 系列
第 18/20 篇
分享: