C# Lambda 表达式详解与应用大全
一. 基本 Lambda 语法
1.1核心概念:
Lambda表达式是一种匿名函数,用于创建委托或表达式树对象。其核心运算符 =>读作“goes to”。
1.2语法结构:
左侧是输入参数列表,可以是 ()、x或 (x, y)。
右侧是表达式体(单行,隐含return)或语句块 {}(多行,需显式return)。
1.3详细说明与示例:
a.无参数Lambda:
语法:() => expression/statement
示例:Action sayHello = () => Console.WriteLine(“Hello, Lambda!”);
特性解析:()表示无参数,右侧表达式体执行一个操作,赋值给Action委托。
b.单参数Lambda(可推断类型):
语法:param => expression
示例:Func square = x => x * x;
特性解析:参数x的类型由委托Func推断为int。右侧表达式x * x的结果自动返回。
c.多参数Lambda:
语法:(param1, param2, ...) => expression
示例:Func add = (a, b) => a + b;
特性解析:多个参数必须用括号()包围。
d.显式指定参数类型:
语法:(Type1 param1, Type2 param2) => expression
示例:Func repeat = (string s, int n) => string.Concat(Enumerable.Repeat(s, n));
特性解析:当编译器无法推断类型时,需显式声明参数类型。
e.语句Lambda(多行):
语法:param => { statement1; statement2; return value; }
示例:
Action printTable = n =>
{
Console.WriteLine($“{n}的乘法表:”);
for (int i = 1; i <= 10; i++) Console.WriteLine($“ {n} × {i} = {n * i}”);
};
特性解析:当逻辑复杂时,使用花括号{}构成语句块。无返回值时用Action,有返回值时用Func并在块内return。
f.带复杂逻辑的语句Lambda:
示例:
Func fizzBuzz = n =>
{
if (n % 15 == 0) return “FizzBuzz”;
if (n % 3 == 0) return “Fizz”;
if (n % 5 == 0) return “Buzz”;
return n.ToString();
};
特性解析:展示了在Lambda语句块中使用条件判断等复杂逻辑。
1.4例程
#region 1. 基本 Lambda 语法
/// <summary>
/// 演示 Lambda 表达式的基本语法结构
/// Lambda 运算符:=> 读作 "goes to" 或 "becomes"
/// 左侧:输入参数列表(可为空、单个或多个参数)
/// 右侧:表达式或语句块(返回值或执行操作)
/// </summary>
static void DemonstrateBasicSyntax()
{
Console.WriteLine("=== 1. 基本 Lambda 语法 ===");
Console.WriteLine();
// 1.1 无参数 Lambda
// 语法:() => expression/statement
// () : 空参数列表,表示不接受任何参数
// => : Lambda 运算符,分隔参数和函数体
// Console.WriteLine(...) : 函数体,执行的操作
Action sayHello = () => Console.WriteLine("Hello, Lambda!");
// Action: 无参数、无返回值的委托类型
// sayHello: 委托变量,存储 Lambda 表达式
sayHello(); // 调用委托,执行 Lambda
// 1.2 单参数 Lambda(参数类型可推断,可省略括号)
// 语法:param => expression
// x : 输入参数,类型由委托自动推断为 int
// x * x : 表达式体,计算结果自动返回
Func<int, int> square = x => x * x;
// Func<int, int>: 泛型委托
// 第一个 int: 输入参数类型
// 第二个 int: 返回值类型
Console.WriteLine($"5 的平方:{square(5)}"); // 传入 5,返回 25
// 1.3 多参数 Lambda
// 语法:(param1, param2, ...) => expression
// (a, b) : 多个参数必须用括号包围
// a + b : 表达式体,返回两数之和
Func<int, int, int> add = (a, b) => a + b;
// Func<int, int, int>:
// 前两个 int: 输入参数类型
// 最后一个 int: 返回值类型
Console.WriteLine($"3 + 7 = {add(3, 7)}"); // 传入 3 和 7,返回 10
// 1.4 显式指定参数类型
// 当编译器无法推断类型时,需要显式声明
// (string s, int n) : 显式指定每个参数的类型
Func<string, int, string> repeat = (string s, int n) => string.Concat(Enumerable.Repeat(s, n));
// string.Concat: 连接字符串
// Enumerable.Repeat(s, n): 生成包含 n 个 s 的序列
Console.WriteLine($"重复字符串:{repeat("C#", 3)}"); // 输出:C#C#C#
// 1.5 语句 Lambda(多行)
// 当需要多条语句时,使用代码块 { }
// n : 输入参数
// { ... } : 语句块,可包含多条语句
Action<int> printMultiplicationTable = n =>
{
Console.WriteLine($"{n}的乘法表:");
for (int i = 1; i <= 10; i++)
{
Console.WriteLine($" {n} × {i} = {n * i}");
}
};
// Action<int>: 接受一个 int 参数,无返回值
printMultiplicationTable(7); // 打印 7 的乘法表
// 1.6 带复杂逻辑的 Lambda
// 使用 if-else 条件判断的语句 Lambda
Func<int, string> fizzBuzz = n =>
{
if (n % 15 == 0) return "FizzBuzz"; // 能被 15 整除
if (n % 3 == 0) return "Fizz"; // 能被 3 整除
if (n % 5 == 0) return "Buzz"; // 能被 5 整除
return n.ToString(); // 其他情况返回数字本身
};
// Func<int, string>: 接受 int,返回 string
Console.WriteLine("FizzBuzz 示例:");
for (int i = 1; i <= 5; i++)
{
Console.WriteLine($" {i}: {fizzBuzz(i)}");
}
}
#endregion
1.5输出
=== 1. 基本 Lambda 语法 ===
Hello, Lambda!
5 的平方:25
3 + 7 = 10
重复字符串:C#C#C#
7的乘法表:
7 × 1 = 7
7 × 2 = 14
7 × 3 = 21
7 × 4 = 28
7 × 5 = 35
7 × 6 = 42
7 × 7 = 49
7 × 8 = 56
7 × 9 = 63
7 × 10 = 70
FizzBuzz 示例:
1: 1
2: 2
3: Fizz
4: 4
5: Buzz
二. 委托与 Lambda
2.1核心概念:
委托的核心作用:实现方法的间接调用。将一个方法“赋值”给一个委托变量,然后通过调用这个变量来调用它背后所引用的方法。这使得方法可以像参数一样被传递,是实现回调函数、事件机制以及LINQ的基石。
委托是Lambda的“容器”或“目标类型”,而Lambda是创建委托实例最简洁的语法形式。 可以将Lambda看作是为实现委托而“生”的。
详细解释:
-
Lambda 表达式本质上是一个匿名方法。它没有自己的名字,但有一个方法体。
-
一个匿名方法(Lambda)必须被赋值给一个具有匹配签名的委托类型变量,才能被存储和调用。委托类型为Lambda提供了所需的“方法签名”信息。
-
Lambda 极大地简化了委托实例的创建过程。在C#早期,创建委托实例需要先定义一个具名方法,然后将其“分配给”委托。Lambda允许在使用的地方即时定义方法逻辑。
.NET提供了Action(无返回值)、Func(有返回值)、Predicate(返回bool)等内置委托。
2.2语法结构:
Lambda的书写语法同上,重点是理解如何赋值给各种委托类型。
2.3详细说明与示例:
a.使用内置委托:
Action示例:Action logMessage = message => Console.WriteLine($“[LOG] {message}”);
Func示例:Func calculateHypotenuse = (a, b) => Math.Sqrt(a * a + b * b);
Predicate示例:Predicate isPrime = n => { … return true/false; };
特性解析:Lambda表达式无缝兼容这些内置委托,简化了代码。
b.自定义委托:
示例:BinaryOperation multiply = (x, y) => x * y;(BinaryOperation是自定义的delegate int BinaryOperation(int a, int b);)
特性解析:Lambda同样可以实现自定义委托类型。
c.多播委托与委托链:
示例:
multiAction += () => Console.WriteLine(“动作 2”);
Action actionToRemove = () => Console.WriteLine("动作 2");multiAction -= actionToRemove;
特性解析:多个Lambda(或方法)可以通过+=运算符组合成一个委托链,调用时按顺序执行。移除委托需要相同的引用才能移除。委托的 +=和 -=操作,比较的是委托实例的引用(内存地址)是否相同,而不是比较它们背后方法(或Lambda)的代码是否相同。虽然两个Lambda表达式 () => Console.WriteLine("动作 2")在代码文本上完全一样,但C#编译器会将每一个独立的Lambda表达式编译成一个全新的、匿名的方法。因此,它们对应的是两个不同的方法实例,在内存中是两个不同的引用。
d.委托作为参数(高阶函数):
示例:ProcessNumbers(data, filter: n => n % 2 == 0, processor: n => Console.WriteLine($“偶数:{n}”));
特性解析:Lambda可以作为参数传递给另一个方法,使该方法的行为由调用方决定,极大地增强了灵活性。
2.4例程
#region 2. 委托与 Lambda
/// <summary>
/// 自定义委托类型 - 二元数学运算
/// delegate: 关键字,定义委托类型
/// int: 返回值类型
/// BinaryOperation: 委托名称
/// (int a, int b): 参数列表
/// </summary>
/// <param name="a">第一个操作数</param>
/// <param name="b">第二个操作数</param>
/// <returns>运算结果</returns>
delegate int BinaryOperation(int a, int b);
/// <summary>
/// 演示委托与 Lambda 的结合使用
/// 委托是 Lambda 的载体,Lambda 是委托的实现
/// </summary>
static void DemonstrateDelegatesAndLambda()
{
Console.WriteLine();
Console.WriteLine("=== 2. 委托与 Lambda ===");
Console.WriteLine();
// 2.1 使用内置委托类型
// Action<T> - 无返回值,有参数
// message: Lambda 参数,类型为 string
// Console.WriteLine(...): 执行的操作,无返回值
Action<string> logMessage = message => Console.WriteLine($"[LOG] {DateTime.Now:HH:mm:ss}: {message}");
// Action<string>:
// string: 唯一的参数类型
// 无返回值(void)
logMessage("程序启动");
logMessage("Lambda 演示开始");
// Func<T1, T2, TResult> - 有返回值
// (a, b): 两个 double 类型参数
// Math.Sqrt(a * a + b * b): 计算斜边长度并返回
Func<double, double, double> calculateHypotenuse = (a, b) => Math.Sqrt(a * a + b * b);
// Func<double, double, double>:
// 第一个 double: 参数 a 的类型
// 第二个 double: 参数 b 的类型
// 第三个 double: 返回值类型
double hypotenuse = calculateHypotenuse(3, 4);
Console.WriteLine($"直角三角形斜边 (3, 4): {hypotenuse}"); // 输出:5
// Predicate<T> - 返回 bool 的委托,用于判断
// n: 输入参数,类型为 int
// { ... }: 语句块,返回 true 或 false
Predicate<int> isPrime = n =>
{
if (n < 2) return false; // 小于 2 不是质数
for (int i = 2; i <= Math.Sqrt(n); i++)
{
if (n % i == 0) return false; // 能被整除不是质数
}
return true; // 其他情况是质数
};
// Predicate<int>: 接受 int,返回 bool
Console.WriteLine("质数检测:");
Console.WriteLine($" 7 是质数?{isPrime(7)}"); // True
Console.WriteLine($" 8 是质数?{isPrime(8)}"); // False
// 2.2 自定义委托
// (x, y): 两个 int 参数
// x * y: 返回乘积
BinaryOperation multiply = (x, y) => x * y;
// BinaryOperation: 自定义委托,等价于 Func<int, int, int>
// (x, y): 两个 int 参数
// (int)Math.Pow(x, y): 计算 x 的 y 次方,转换为 int
BinaryOperation power = (x, y) => (int)Math.Pow(x, y);
Console.WriteLine("自定义委托示例:");
Console.WriteLine($" 5 * 3 = {multiply(5, 3)}"); // 15
Console.WriteLine($" 2 ^ 8 = {power(2, 8)}"); // 256
// 2.3 委托链(多播委托)
// 多个委托可以组合在一起,按顺序执行
Action? multiAction = () => Console.WriteLine("动作 1");
// ?: 可空引用类型,允许为 null
multiAction += () => Console.WriteLine("动作 2"); // 添加第二个动作
multiAction += () => Console.WriteLine("动作 3"); // 添加第三个动作
Console.WriteLine("多播委托执行:");
multiAction(); // 依次执行所有动作
// 获取委托列表 - 查看委托链中的所有方法
Delegate[] delegates = multiAction.GetInvocationList();
Console.WriteLine($"委托链中有 {delegates.Length} 个方法");
// 移除委托 - 注意:需要相同的引用才能移除
Action actionToRemove = () => Console.WriteLine("动作 2");
// 这里不会移除,因为 actionToRemove 是不同的引用实例
multiAction -= actionToRemove;
// 2.4 委托作为参数
// 静态局部方法,接受过滤器和处理器委托
static void ProcessNumbers(int[] numbers, Func<int, bool> filter, Action<int> processor)
{
foreach (int n in numbers)
{
if (filter(n)) // 调用过滤器委托
{
processor(n); // 调用处理器委托
}
}
}
// int[] numbers: 整数数组
// Func<int, bool> filter: 过滤条件,接受 int 返回 bool
// Action<int> processor: 处理操作,接受 int 无返回值
int[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Console.WriteLine("委托作为参数 - 处理偶数:");
ProcessNumbers(data,
filter: n => n % 2 == 0, // Lambda: 判断是否为偶数
processor: n => Console.WriteLine($" 偶数:{n}")); // Lambda: 打印
Console.WriteLine("委托作为参数 - 处理平方数:");
ProcessNumbers(data,
filter: n => Math.Sqrt(n) % 1 == 0, // Lambda: 判断是否为完全平方数
processor: n => Console.WriteLine($" 平方数:{n}"));
}
#endregion
2.5输出
=== 2. 委托与 Lambda ===
[LOG] 13:25:19: 程序启动
[LOG] 13:25:19: Lambda 演示开始
直角三角形斜边 (3, 4): 5
质数检测:
7 是质数?True
8 是质数?False
自定义委托示例:
5 * 3 = 15
2 ^ 8 = 256
多播委托执行:
动作 1
动作 2
动作 3
委托链中有 3 个方法
委托作为参数 - 处理偶数:
偶数:2
偶数:4
偶数:6
偶数:8
偶数:10
委托作为参数 - 处理平方数:
平方数:1
平方数:4
平方数:9
三.闭包与变量捕获
3.1核心概念:
闭包(Closure)指Lambda表达式能够“捕获”其所在作用域中的外部变量,并延长该变量的生命周期。捕获的是变量的引用,而非值的副本。
3.2语法结构:
Lambda直接使用外部变量。
3.3详细说明与示例:
a.基本闭包:
示例:
int baseValue = 10;
Func addBase = x => x + baseValue; // 捕获baseValue
baseValue = 20; // 修改会影响Lambda
Console.WriteLine(addBase(5)); // 输出25,而非15
特性解析:addBase捕获了外部变量baseValue。后续对baseValue的修改会影响已创建的Lambda的行为。
b.循环中的闭包陷阱与解决方案:
错误示例:badClosures.Add(() => i * 10);// 所有Lambda捕获同一个变量i,最终i=5,所有结果都是50。
正确示例:int captured = i; goodClosures.Add(() => captured * 10);// 每次循环创建新变量captured供Lambda捕获。
特性解析:在循环中创建Lambda时,若直接捕获迭代变量,所有Lambda共享该变量的最终值。需在循环内创建局部变量副本再捕获。
c.闭包与状态封装:
示例:Func CreateCounter = () => { int count = 0; return () => ++count; };
特性解析:工厂函数返回的Lambda捕获了局部变量count。每次调用工厂函数都会创建一个独立的count变量,从而实现多个独立的计数器。这体现了闭包如何用于封装状态。
d.闭包捕获复杂对象:
示例:
Func<string> getInfo = () => $"{person.Name} 今年 {person.Age} 岁";
Func<string> getMutableInfo = () => $"{mutablePerson.Name} 今年 {mutablePerson.Age} 岁";
特性解析:由于闭包(Lambda)持有的是同一个对象的引用,所以当通过这个闭包(调用 getMutableInfo())访问对象属性时,它“看到”的是对象最新的、已被修改的状态。
3.4例程
#region 3. 闭包与变量捕获
/// <summary>
/// 演示闭包(Closure)与变量捕获的特性
/// 闭包:Lambda 捕获外部变量,延长其生命周期
/// 捕获的是变量的引用,而非值
/// </summary>
static void DemonstrateClosures()
{
Console.WriteLine();
Console.WriteLine("=== 3. 闭包与变量捕获 ===");
Console.WriteLine();
// 3.1 基本闭包
Console.WriteLine("3.1 基本闭包:");
int baseValue = 10; // 外部变量
// Lambda 捕获了 baseValue 变量
// x: Lambda 参数
// x + baseValue: 使用捕获的外部变量
Func<int, int> addBase = x => x + baseValue;
Console.WriteLine($" addBase(5) = {addBase(5)} (10 + 5)");
// 修改捕获的变量会影响 Lambda 的行为
// 因为捕获的是引用,不是值的副本
baseValue = 20;
Console.WriteLine($" addBase(5) = {addBase(5)} (20 + 5)");
// 3.2 循环中的闭包问题 - 经典陷阱
Console.WriteLine();
Console.WriteLine("3.2 循环中的闭包问题:");
List<Func<int>> badClosures = new List<Func<int>>();
List<Func<int>> goodClosures = new List<Func<int>>();
// Func<int>: 无参数,返回 int 的委托
for (int i = 0; i < 5; i++)
{
// 错误的做法 - 所有 Lambda 都引用同一个变量 i
// 循环结束时 i=5,所有 Lambda 都返回 50
badClosures.Add(() => i * 10);
// () => i * 10: 捕获的是变量 i 本身,不是当前值
// 正确的做法 - 创建局部变量副本
int captured = i; // 每次循环创建新变量
goodClosures.Add(() => captured * 10);
// () => captured * 10: 捕获的是当前循环的 captured 值
}
Console.WriteLine(" 错误的闭包结果 (都使用最终的 i 值):");
foreach (var func in badClosures)
{
Console.WriteLine($" {func()}"); // 都输出 50
}
Console.WriteLine(" 正确的闭包结果 (捕获时的值):");
foreach (var func in goodClosures)
{
Console.WriteLine($" {func()}"); // 输出 0, 10, 20, 30, 40
}
// 3.3 闭包的内存行为
Console.WriteLine();
Console.WriteLine("3.3 闭包的内存行为:");
// 工厂函数返回一个计数器 Lambda
Func<Func<int>> CreateCounter = () =>
{
int count = 0; // 局部变量被 Lambda 捕获
// 返回的 Lambda 捕获了 count 变量
return () => ++count;
// () => ++count: 无参数,返回递增后的 count
};
// Func<Func<int>>: 返回 Func<int> 的委托
var counter1 = CreateCounter(); // 创建独立的计数器实例
var counter2 = CreateCounter(); // 每个实例有自己的 count 变量
Console.WriteLine(" 两个独立的计数器:");
Console.WriteLine($" Counter1: {counter1()}, {counter1()}, {counter1()}"); // 1, 2, 3
Console.WriteLine($" Counter2: {counter2()}, {counter2()}"); // 1, 2
Console.WriteLine($" Counter1: {counter1()}"); // 4(继续计数)
Console.WriteLine($" Counter2: {counter2()}"); // 3(继续计数)
// 3.4 闭包捕获复杂对象
Console.WriteLine();
Console.WriteLine("3.4 捕获复杂对象:");
// 匿名类型 - 属性是只读的
var person = new { Name = "Alice", Age = 30 };
// new { Name = ..., Age = ... }: 匿名类型,编译器自动生成类
Func<string> getInfo = () => $"{person.Name} 今年 {person.Age} 岁";
// () => ...: 捕获 person 对象的引用
Console.WriteLine($" 初始:{getInfo()}");
// 可变对象 - 修改会影响 Lambda
var mutablePerson = new Person { Name = "Bob", Age = 25 };
Func<string> getMutableInfo = () => $"{mutablePerson.Name} 今年 {mutablePerson.Age} 岁";
Console.WriteLine($" 修改前:{getMutableInfo()}");
mutablePerson.Age = 26; // 修改对象属性
Console.WriteLine($" 修改后:{getMutableInfo()}"); // Lambda 看到修改后的值
}
#endregion
3.5输出
=== 3. 闭包与变量捕获 ===
3.1 基本闭包:
addBase(5) = 15 (10 + 5)
addBase(5) = 25 (20 + 5)
3.2 循环中的闭包问题:
错误的闭包结果 (都使用最终的 i 值):
50
50
50
50
50
正确的闭包结果 (捕获时的值):
0
10
20
30
40
3.3 闭包的内存行为:
两个独立的计数器:
Counter1: 1, 2, 3
Counter2: 1, 2
Counter1: 4
Counter2: 3
3.4 捕获复杂对象:
初始:Alice 今年 30 岁
修改前:Bob 今年 25 岁
修改后:Bob 今年 26 岁
四.LINQ 与 Lambda
4.1核心概念:
LINQ(语言集成查询)的方法语法大量依赖Lambda表达式来定义查询、过滤、排序、投影、聚合等操作,其核心是链式调用一系列扩展方法,而 Lambda 表达式则为这些方法提供了关键的操作逻辑,Lambda 以最简洁的形式,就地实现了这些方法所必需的委托参数,使得声明式查询得以在 C# 中以流畅的代码形式表达。
4.2语法结构:
集合.操作符(参数 => 表达式)
4.3详细说明与示例:
a.基本操作(Where, OrderBy, Select):
示例:employees.Where(e => e.Salary > 12000).OrderByDescending(e => e.Salary).Select(e => new { e.Name, e.Salary });
特性解析:
e => e.Salary > 12000:作为Where的谓词,筛选元素。
e => e.Salary:作为OrderByDescending的键选择器,指定排序依据。
e => new { e.Name, e.Salary }:作为Select的结果选择器,定义投影形状(匿名类型)。
b.聚合操作(Sum, Average):
示例:employees.Sum(e => e.Salary)
特性解析:e => e.Salary作为选择器,指定要对哪个字段进行聚合计算。
c.分组操作(GroupBy):
示例:employees.GroupBy(e => e.Department)
特性解析:e => e.Department作为键选择器,指定按哪个属性进行分组。
d.连接操作(Join):
示例:employees.Join(projects, e => e.Id, p => p.LeaderId, (e, p) => new { e.Name, p.Name })
特性解析:使用三个Lambda:e => e.Id(外键)、p => p.LeaderId(内键)、(e, p) => new { … }(结果构造器)。
e.自定义扩展方法:
示例:employees.TopN(e => e.Salary, 3)
特性解析:展示了如何编写自己的扩展方法(如TopN)来接收Lambda参数(Func keySelector),实现更灵活的查询。
4.4例程
#region 4. LINQ 与 Lambda
/// <summary>
/// 演示 LINQ (Language Integrated Query) 与 Lambda 的结合使用
/// LINQ 方法语法本质上是扩展方法 + Lambda 表达式
/// </summary>
static void DemonstrateLINQAndLambda()
{
Console.WriteLine();
Console.WriteLine("=== 4. LINQ 与 Lambda ===");
Console.WriteLine();
// 准备测试数据 - 员工列表
var employees = new List<Employee>
{
new Employee { Id = 1, Name = "张三", Department = "研发部", Salary = 15000, Age = 30 },
new Employee { Id = 2, Name = "李四", Department = "研发部", Salary = 12000, Age = 25 },
new Employee { Id = 3, Name = "王五", Department = "市场部", Salary = 10000, Age = 28 },
new Employee { Id = 4, Name = "赵六", Department = "市场部", Salary = 11000, Age = 32 },
new Employee { Id = 5, Name = "孙七", Department = "人事部", Salary = 9000, Age = 26 },
new Employee { Id = 6, Name = "周八", Department = "研发部", Salary = 18000, Age = 35 },
new Employee { Id = 7, Name = "吴九", Department = "财务部", Salary = 13000, Age = 40 },
new Employee { Id = 8, Name = "郑十", Department = "研发部", Salary = 14000, Age = 29 }
};
// 4.1 基本 LINQ 查询
// 查询语法 - 类似 SQL 的语法风格(语法糖)
var highSalaryEmployees =
from e in employees // 数据源
where e.Salary > 12000 // 筛选条件
orderby e.Salary descending // 排序
select new { e.Name, e.Salary }; // 投影(选择字段)
Console.WriteLine("4.1 基本 LINQ 查询:");
Console.WriteLine(" 高薪员工 (查询语法):");
foreach (var emp in highSalaryEmployees)
{
Console.WriteLine($" {emp.Name}: ¥{emp.Salary:F2}");
}
// 方法语法 (Lambda) - 编译器将查询语法转换为此形式
// .Where(e => e.Salary > 12000)
// e => e.Salary > 12000: Lambda 作为 Where 方法的参数
// e: 集合中的每个元素
// e.Salary > 12000: 返回 bool 的条件表达式
var highSalaryEmployeesLambda = employees
.Where(e => e.Salary > 12000) // Where: 过滤
.OrderByDescending(e => e.Salary) // OrderByDescending: 降序排序
.Select(e => new { e.Name, e.Salary }); // Select: 投影为匿名类型
Console.WriteLine(" 高薪员工 (方法语法/Lambda):");
highSalaryEmployeesLambda.ToList().ForEach(e =>
Console.WriteLine($" {e.Name}: ¥{e.Salary:F2}"));
// 4.2 聚合操作
Console.WriteLine();
Console.WriteLine("4.2 聚合操作:");
// Sum(e => e.Salary)
// e => e.Salary: 选择器 Lambda,指定要累加的字段
double totalSalary = employees.Sum(e => e.Salary);
// Average(e => e.Salary): 计算平均值
double avgSalary = employees.Average(e => e.Salary);
// Max(e => e.Salary): 找最大值
double maxSalary = employees.Max(e => e.Salary);
// Min(e => e.Salary): 找最小值
double minSalary = employees.Min(e => e.Salary);
Console.WriteLine($" 总工资:¥{totalSalary:F2}");
Console.WriteLine($" 平均工资:¥{avgSalary:F2}");
Console.WriteLine($" 最高工资:¥{maxSalary:F2}");
Console.WriteLine($" 最低工资:¥{minSalary:F2}");
// 4.3 分组操作
Console.WriteLine();
Console.WriteLine("4.3 分组操作:");
// GroupBy(e => e.Department)
// e => e.Department: 键选择器 Lambda,按部门分组
var byDepartment = employees
.GroupBy(e => e.Department)
.Select(g => new // g: IGrouping<string, Employee> 类型
{
Department = g.Key, // 分组键(部门名称)
Count = g.Count(), // 该部门人数
AvgSalary = g.Average(e => e.Salary), // 平均工资
Employees = g.Select(e => e.Name).ToList() // 员工姓名列表
});
foreach (var dept in byDepartment)
{
Console.WriteLine($" {dept.Department}: {dept.Count}人,平均工资:¥{dept.AvgSalary:F2}");
Console.WriteLine($" 员工:{string.Join(", ", dept.Employees)}");
}
// 4.4 连接操作
Console.WriteLine();
Console.WriteLine("4.4 连接操作:");
var projects = new List<Project>
{
new Project { Id = 1, Name = "ERP 系统", LeaderId = 1 },
new Project { Id = 2, Name = "移动应用", LeaderId = 2 },
new Project { Id = 3, Name = "数据分析", LeaderId = 1 }
};
// Join 方法签名:
// Join<TOuter, TInner, TKey, TResult>(
// inner, // 内层集合
// outerKeySelector, // 外层键选择器
// innerKeySelector, // 内层键选择器
// resultSelector // 结果选择器
// )
var employeeProjects = employees
.Join(projects,
e => e.Id, // 外层键选择器:员工的 Id
p => p.LeaderId, // 内层键选择器:项目的 LeaderId
(e, p) => new { e.Name, Project = p.Name }); // 结果选择器 Lambda
Console.WriteLine(" 项目负责人:");
employeeProjects.ToList().ForEach(ep =>
Console.WriteLine($" {ep.Name} -> {ep.Project}"));
// 4.5 复合查询
Console.WriteLine();
Console.WriteLine("4.5 复合查询:");
// 链式调用多个 LINQ 方法
var seniorDevelopers = employees
.Where(e => e.Department == "研发部") // 筛选研发部
.Where(e => e.Age >= 30) // 年龄>=30
.Where(e => e.Salary >= 15000) // 工资>=15000
.OrderByDescending(e => e.Salary) // 按工资降序
.ThenBy(e => e.Name) // 再按姓名升序
.Select(e => new
{
e.Name,
e.Age,
e.Salary,
SalaryLevel = e.Salary >= 17000 ? "高级" : "中级" // 三元运算符
});
Console.WriteLine(" 资深研发人员:");
seniorDevelopers.ToList().ForEach(e =>
Console.WriteLine($" {e.Name}, {e.Age}岁,¥{e.Salary:F2} ({e.SalaryLevel})"));
// 4.6 自定义扩展方法
Console.WriteLine();
Console.WriteLine("4.6 自定义扩展方法:");
// TopN(e => e.Salary, 3)
// e => e.Salary: 键选择器 Lambda
// 3: 取前 3 个
var top3 = employees.TopN(e => e.Salary, 3);
Console.WriteLine(" 工资 Top3:");
top3.ToList().ForEach(e =>
Console.WriteLine($" {e.Name}: ¥{e.Salary:F2}"));
}
#endregion
4.5输出
=== 4. LINQ 与 Lambda ===
4.1 基本 LINQ 查询:
高薪员工 (查询语法):
周八: ¥18000.00
张三: ¥15000.00
郑十: ¥14000.00
吴九: ¥13000.00
高薪员工 (方法语法/Lambda):
周八: ¥18000.00
张三: ¥15000.00
郑十: ¥14000.00
吴九: ¥13000.00
4.2 聚合操作:
总工资:¥102000.00
平均工资:¥12750.00
最高工资:¥18000.00
最低工资:¥9000.00
4.3 分组操作:
研发部: 4人,平均工资:¥14750.00
员工:张三, 李四, 周八, 郑十
市场部: 2人,平均工资:¥10500.00
员工:王五, 赵六
人事部: 1人,平均工资:¥9000.00
员工:孙七
财务部: 1人,平均工资:¥13000.00
员工:吴九
4.4 连接操作:
项目负责人:
张三 -> ERP 系统
张三 -> 数据分析
李四 -> 移动应用
4.5 复合查询:
资深研发人员:
周八, 35岁,¥18000.00 (高级)
张三, 30岁,¥15000.00 (中级)
4.6 自定义扩展方法:
工资 Top3:
周八: ¥18000.00
张三: ¥15000.00
郑十: ¥14000.00
五.表达式树 (Expression Trees)
5.1核心概念:
表达式树将Lambda表达式表示为可检查、可修改的数据结构(树形节点),而非可执行代码。常用于动态构建查询(如Entity Framework)、代码生成等场景。
为什么需要表达式树?(与Lambda委托的关键区别):
委托Lambda:是黑盒。你只能传入参数并得到结果,但无法知道它内部是如何计算的。它是一系列CPU指令。
表达式树Lambda:是白盒,是源代码的抽象语法树。你可以分析、遍历、修改这棵树的节点。然后可以:
编译成委托执行(如上文的 .Compile())。
翻译成其他语言(如SQL)。这是 Entity Framework 等ORM框架的核心原理。当在LINQ查询中写 Where(e => e.Salary > 12000)并针对 IQueryable数据源时,这个Lambda会被转换为表达式树。EF Core的提供者会分析这棵树,并将其翻译成 WHERE Salary > 12000这样的SQL语句发送到数据库,而不是在内存中过滤。
5.2语法结构:
使用Expression>类型声明,或通过System.Linq.Expressions命名空间下的API动态构建。System.Linq.Expressions.Expression类提供了一系列静态方法,用于以编程方式创建表达式树的各个节点。每个方法都返回一个从 Expression派生的特定节点类型对象,这些对象共同构成一棵描述代码逻辑的树。
a.Expression.Parameter
作用:创建一个表示 Lambda 表达式参数 的节点。
文档示例:ParameterExpression paramX = Expression.Parameter(typeof(int), “x”);
解析:此方法定义了一个变量(如 x),并指定其类型(typeof(int))和可选名称(“x””)。它是构建Lambda表达式树的起点,代表输入的占位符。
b.Expression.Constant
作用:创建一个表示 常量值 的节点。
文档示例:ConstantExpression constant2 = Expression.Constant(2, typeof(int));
解析:此方法将一个字面量(如数字2、字符串“Hello”)封装为表达式树中的一个叶子节点。它代表计算中不变的部分。
c.Expression.Add/ Expression.Multiply
作用:创建表示 算术运算 的二元表达式节点。Add为加法,Multiply为乘法。同类方法还有 Subtract(减)、Divide(除)等。
文档示例:BinaryExpression addBody = Expression.Add(paramX, paramY);(构建 x + y)
解析:这些方法接收左、右两个表达式节点作为子节点,并返回一个新的二元运算节点。它们是构建复杂计算逻辑的桥梁。
d.Expression.GreaterThan/ Expression.LessThan
作用:创建表示 关系比较 的二元表达式节点。GreaterThan为“大于”,LessThan为“小于”。
文档示例(5.3节):BinaryExpression greaterThan = Expression.GreaterThan(paramNum, zero);(构建 num > 0)
解析:与算术运算类似,它们比较两个子表达式的结果,返回一个表示 bool类型比较结果的节点。
e.Expression.AndAlso
作用:创建表示 逻辑“与” 运算的二元表达式节点。它对应C#中的 &&运算符,支持短路求值(一旦能够根据左侧操作数的值确定整个表达式的结果,就会立即停止计算,不再对右侧操作数进行求值。)。
文档示例(5.6节):BinaryExpression andExpression = Expression.AndAlso(ageGreaterThan20, ageLessThan30);(构建 ... && ...)
解析:用于组合两个返回 bool的表达式节点,形成逻辑与条件。
f.Expression.Call
作用:创建一个表示 实例方法或静态方法调用 的节点。
文档示例(5.4节):MethodCallExpression toUpperCall = Expression.Call(paramStr, toUpperMethod);(构建 s.ToUpper())
解析:此方法需要指定方法所属的对象实例(对于实例方法)、要调用的方法的 MethodInfo(通过反射获得),以及方法的参数列表。它用于在表达式树中嵌入方法调用逻辑。
g.Expression.Lambda
作用:将一系列表达式节点封装成一个完整的、可编译的 Lambda 表达式树。
文档示例:Expression> multiplyExpr = Expression.Lambda>(multiplyBody, paramX, paramY);
解析:这是构建过程的最后一步。它接收一个表达式体(如 multiplyBody)和一组参数定义(如 paramX, paramY),将它们组合成一个代表匿名函数的 Expression对象。此对象可以进行分析、修改,或通过 .Compile()方法编译成可执行的委托。
5.3详细说明与示例:
a.声明表达式树:
示例:Expression> addExpr = (x, y) => x + y;
特性解析:与委托Lambda相似,但类型是Expression>。它可以被编译成委托执行(addExpr.Compile()(3,5)),也可以被分析其结构。
b.动态构建表达式树:
示例:
ParameterExpression paramX = Expression.Parameter(typeof(int), “x”);
BinaryExpression addBody = Expression.Add(paramX, Expression.Constant(2));
Expression> addTwoExpr = Expression.Lambda>(addBody, paramX);
特性解析:通过Expression类的静态方法(Parameter, Add, Constant, Lambda)手动构建一个表示x => x + 2的表达式树。这展示了表达式树作为数据结构的本质。
c.在IQueryable查询中的应用:
示例:people.AsQueryable().Where(filterExpr);(filterExpr是一个Expression>)
特性解析:IQueryable提供者(如EF Core)可以解析filterExpr这个表达式树,将其转换为特定的查询语言(如SQL),而不是在内存中过滤。这是LINQ to SQL等技术的基石。
5.4例程
#region 5. 表达式树 (Expression Trees)
/// <summary>
/// 演示表达式树(Expression Tree)的创建和使用
/// 表达式树:将 Lambda 表达式表示为数据结构(而非可执行代码)
/// 可用于动态修改代码、ORM 框架(如 Entity Framework)等
/// </summary>
static void DemonstrateExpressionTrees()
{
Console.WriteLine();
Console.WriteLine("=== 5. 表达式树 (Expression Trees) ===");
Console.WriteLine();
// 5.1 创建简单表达式树
Console.WriteLine("5.1 创建简单表达式树:");
// ParameterExpression: 表示 Lambda 的参数
// typeof(int): 参数类型
// "x": 参数名称
ParameterExpression paramX = Expression.Parameter(typeof(int), "x");
ParameterExpression paramY = Expression.Parameter(typeof(int), "y");
// BinaryExpression: 表示二元运算(如加法)
// Expression.Add: 创建加法表达式节点
BinaryExpression addBody = Expression.Add(paramX, paramY);
// 这构建了表达式树的主体:x + y
// Expression.Lambda: 将表达式体和参数组合成 Lambda
// Func<int, int, int>: Lambda 的委托类型
Expression<Func<int, int, int>> addExpr =
Expression.Lambda<Func<int, int, int>>(addBody, paramX, paramY);
// Compile(): 将表达式树编译为可执行的委托
Func<int, int, int> addFunc = addExpr.Compile();
Console.WriteLine($" 表达式树:{addExpr}");
Console.WriteLine($" 计算结果:3 + 5 = {addFunc(3, 5)}");
// 5.2 动态构建复杂表达式
Console.WriteLine();
Console.WriteLine("5.2 动态构建复杂表达式:");
// 构建表达式:(x, y) => (x + y) * 2
BinaryExpression addExpr2 = Expression.Add(paramX, paramY); // x + y
ConstantExpression constant2 = Expression.Constant(2, typeof(int)); // 常量 2
BinaryExpression multiplyBody = Expression.Multiply(addExpr2, constant2); // (x+y) * 2
Expression<Func<int, int, int>> multiplyExpr =
Expression.Lambda<Func<int, int, int>>(multiplyBody, paramX, paramY);
Func<int, int, int> multiplyFunc = multiplyExpr.Compile();
Console.WriteLine($" 表达式树:{multiplyExpr}");
Console.WriteLine($" 计算结果:(3 + 5) * 2 = {multiplyFunc(3, 5)}");
// 5.3 条件表达式
Console.WriteLine();
Console.WriteLine("5.3 条件表达式:");
// 构建表达式:num => (num > 0 ? "正数" : "非正数")
ParameterExpression paramNum = Expression.Parameter(typeof(int), "num");
ConstantExpression zero = Expression.Constant(0, typeof(int)); // 常量 0
BinaryExpression greaterThan = Expression.GreaterThan(paramNum, zero); // num > 0
ConstantExpression positive = Expression.Constant("正数", typeof(string));
ConstantExpression nonPositive = Expression.Constant("非正数", typeof(string));
// ConditionalExpression: 三元运算符
// 如果 greaterThan 为 true,返回 positive,否则返回 nonPositive
ConditionalExpression conditional = Expression.Condition(greaterThan, positive, nonPositive);
Expression<Func<int, string>> checkExpr =
Expression.Lambda<Func<int, string>>(conditional, paramNum);
Func<int, string> checkFunc = checkExpr.Compile();
Console.WriteLine($" 表达式树:{checkExpr}");
Console.WriteLine($" checkFunc(5) = {checkFunc(5)}");
Console.WriteLine($" checkFunc(-3) = {checkFunc(-3)}");
Console.WriteLine($" checkFunc(0) = {checkFunc(0)}");
// 5.4 方法调用表达式
Console.WriteLine();
Console.WriteLine("5.4 方法调用表达式:");
// 构建表达式:s => s.ToUpper()
ParameterExpression paramStr = Expression.Parameter(typeof(string), "s");
// 获取 ToUpper 方法的 MethodInfo(反射)
MethodInfo toUpperMethod = typeof(string).GetMethod("ToUpper", Type.EmptyTypes)
?? throw new InvalidOperationException("未找到 ToUpper 方法");
// MethodCallExpression: 方法调用表达式
// Expression.Call: 创建方法调用节点
MethodCallExpression toUpperCall = Expression.Call(paramStr, toUpperMethod);
Expression<Func<string, string>> toUpperExpr =
Expression.Lambda<Func<string, string>>(toUpperCall, paramStr);
Func<string, string> toUpperFunc = toUpperExpr.Compile();
Console.WriteLine($" 'hello' -> '{toUpperFunc("hello")}'");
// 5.5 动态构建查询表达式
Console.WriteLine();
Console.WriteLine("5.5 动态构建查询表达式:");
var people = new List<Person>
{
new Person { Name = "Alice", Age = 30 },
new Person { Name = "Bob", Age = 25 },
new Person { Name = "Charlie", Age = 35 },
new Person { Name = "David", Age = 20 }
};
// 动态构建表达式:p => p.Age > 25
ParameterExpression paramPerson = Expression.Parameter(typeof(Person), "p");
// MemberExpression: 访问属性/字段
// Expression.Property: 创建属性访问节点
MemberExpression ageProperty = Expression.Property(paramPerson, "Age");
ConstantExpression age25 = Expression.Constant(25, typeof(int));
BinaryExpression ageGreaterThan25 = Expression.GreaterThan(ageProperty, age25);
Expression<Func<Person, bool>> filterExpr =
Expression.Lambda<Func<Person, bool>>(ageGreaterThan25, paramPerson);
Console.WriteLine($" 动态构建的过滤器:{filterExpr}");
// AsQueryable(): 将集合转换为 IQueryable,支持表达式树
// Where(filterExpr): 使用表达式树作为过滤条件
var adults = people.AsQueryable().Where(filterExpr);
Console.WriteLine(" 年龄大于 25 的人:");
adults.ToList().ForEach(p => Console.WriteLine($" {p.Name} ({p.Age}岁)"));
// 5.6 组合表达式
Console.WriteLine();
Console.WriteLine("5.6 组合表达式:");
// 构建表达式:p => p.Age > 20 && p.Age < 30
ConstantExpression age20 = Expression.Constant(20, typeof(int));
ConstantExpression age30 = Expression.Constant(30, typeof(int));
BinaryExpression ageGreaterThan20 = Expression.GreaterThan(ageProperty, age20);
BinaryExpression ageLessThan30 = Expression.LessThan(ageProperty, age30);
// AndAlso: 逻辑与 (&&),短路求值
BinaryExpression andExpression = Expression.AndAlso(ageGreaterThan20, ageLessThan30);
Expression<Func<Person, bool>> ageRangeExpr =
Expression.Lambda<Func<Person, bool>>(andExpression, paramPerson);
Console.WriteLine($" 组合表达式:{ageRangeExpr}");
var youngAdults = people.AsQueryable().Where(ageRangeExpr);
Console.WriteLine(" 年龄 20-30 之间的人:");
youngAdults.ToList().ForEach(p => Console.WriteLine($" {p.Name} ({p.Age}岁)"));
}
#endregion
5.5输出
=== 5. 表达式树 (Expression Trees) ===
5.1 创建简单表达式树:
表达式树:(x, y) => (x + y)
计算结果:3 + 5 = 8
5.2 动态构建复杂表达式:
表达式树:(x, y) => ((x + y) * 2)
计算结果:(3 + 5) * 2 = 16
5.3 条件表达式:
表达式树:num => IIF((num > 0), "正数", "非正数")
checkFunc(5) = 正数
checkFunc(-3) = 非正数
checkFunc(0) = 非正数
5.4 方法调用表达式:
'hello' -> 'HELLO'
5.5 动态构建查询表达式:
动态构建的过滤器:p => (p.Age > 25)
年龄大于 25 的人:
Alice (30岁)
Charlie (35岁)
5.6 组合表达式:
组合表达式:p => ((p.Age > 20) AndAlso (p.Age < 30))
年龄 20-30 之间的人:
Bob (25岁)
六.事件与 Lambda
6.1核心概念:
事件是基于委托的发布-订阅模型。Lambda表达式可以简洁地定义事件处理程序(订阅者)。
6.2语法结构:
事件对象.事件名 += (sender, eventArgs) => { … };
6.3详细说明与示例:
a.订阅事件:
示例:processor.DataReceived += (sender, data) => Console.WriteLine($“收到数据:{data}”);
特性解析:使用Lambda快速定义一个匿名事件处理方法。sender是事件源,data是事件参数。
b.取消订阅:
注意:若要取消订阅,必须保存Lambda的引用。-=需要完全相同的委托实例,而匿名Lambda每次都是新实例。
示例:
EventHandler specificHandler = (sender, data) => { … };
processor.DataReceived += specificHandler; // 订阅
processor.DataReceived -= specificHandler; // 取消订阅(有效)
c.Lambda vs 匿名方法:
Lambda示例:processor.DataReceived += (sender, data) => { … };
匿名方法示例:processor.DataReceived += delegate (object? sender, string data) { … };
特性解析:Lambda是更现代、简洁的语法。匿名方法是C# 2.0的语法,需要delegate关键字和显式参数类型声明。
6.4例程
#region 6. 事件与 Lambda
/// <summary>
/// 演示事件(Event)与 Lambda 表达式的结合使用
/// 事件是基于委托的发布 - 订阅模式实现
/// Lambda 简化了事件处理器的编写
/// </summary>
static void DemonstrateEventsAndLambda()
{
Console.WriteLine();
Console.WriteLine("=== 6. 事件与 Lambda ===");
Console.WriteLine();
// 6.1 事件定义
var processor = new DataProcessor();
// 6.2 Lambda 事件处理器
Console.WriteLine("6.2 Lambda 事件处理器:");
// 使用 Lambda 订阅事件
// 当 DataProcessor接收到新数据时,触发此事件,通知所有订阅者。
// 通过 +=运算符,Lambda 被注册(订阅)到对应的事件“频道”上。当事件被触发时,所有订阅的 Lambda 会自动执行,做出响应。
// (sender, data): 事件处理器的两个参数
// sender: 事件发送者(通常是 this)
// data: 事件数据
processor.DataReceived += (sender, data) =>
{
Console.WriteLine($" [Lambda] 收到数据:{data}");
};
// +=: 订阅事件(添加委托到 invocation list)
// 另一个事件处理器
// 当 DataProcessor完成对一批数据的处理后,触发此事件,通知订阅者处理结果。
processor.DataProcessed += (sender, e) =>
{
Console.WriteLine($" [Lambda] 数据处理完成:{e.Result}");
};
// e: ProcessingEventArgs 类型的事件参数对象
// 6.3 处理带参数的事件
// 在数据处理过程中,定期触发此事件以报告当前进度。
// progress: int 类型的进度值(0-100)
processor.ProgressChanged += (sender, progress) =>
{
if (progress % 20 == 0) // 每 20% 报告一次
{
Console.WriteLine($" [Lambda] 处理进度:{progress}%");
}
};
// 6.4 取消事件订阅
// 先保存引用,以便后续取消订阅
EventHandler<string> specificHandler = (sender, data) =>
{
Console.WriteLine($" [特定处理器] 数据:{data}");
};
processor.DataReceived += specificHandler; // 订阅
// 6.5 触发事件
Console.WriteLine("触发事件:");
//模拟进度
processor.StartProcessing("测试数据 123");
// 6.6 取消订阅
// -=: 从事件的 invocation list 中移除委托
processor.DataReceived -= specificHandler;
Console.WriteLine("移除特定处理器后再次触发:");
processor.StartProcessing("新数据 456");
// 6.7 匿名方法 vs Lambda
Console.WriteLine();
Console.WriteLine("6.7 匿名方法 vs Lambda:");
// 匿名方法 (C# 2.0) - 较旧的语法
processor.DataReceived += delegate (object? sender, string data)
{
Console.WriteLine($" [匿名方法] 数据:{data}");
};
// delegate: 匿名方法关键字
// (object? sender, string data): 显式指定参数类型
// ?: 可空引用类型
// Lambda 表达式 (C# 3.0+) - 更简洁的语法
processor.DataReceived += (sender, data) =>
{
Console.WriteLine($" [Lambda] 数据:{data}");
};
processor.ProcessSingleData("比较数据");
// 6.8 自定义事件参数
Console.WriteLine();
Console.WriteLine("6.8 自定义事件参数:");
var sensor = new Sensor();
// ReadingChanged 事件携带详细的事件参数
sensor.ReadingChanged += (sender, e) =>
{
Console.WriteLine($" 传感器读数变化:{e.OldValue} -> {e.NewValue}");
Console.WriteLine($" 变化时间:{e.ChangeTime}");
};
// e: ReadingChangedEventArgs 类型
// e.OldValue: 旧值
// e.NewValue: 新值
// e.ChangeTime: 变更时间
// 修改属性会触发事件
sensor.Reading = 25.5;
sensor.Reading = 26.0;
sensor.Reading = 25.8;
}
#endregion
6.5输出
=== 6. 事件与 Lambda ===
6.2 Lambda 事件处理器:
触发事件:
[Lambda] 收到数据:测试数据 123
[特定处理器] 数据:测试数据 123
[Lambda] 处理进度:0%
[Lambda] 处理进度:20%
[Lambda] 处理进度:40%
[Lambda] 处理进度:60%
[Lambda] 处理进度:80%
[Lambda] 处理进度:100%
[Lambda] 数据处理完成:处理结果:测试数据 123
移除特定处理器后再次触发:
[Lambda] 收到数据:新数据 456
[Lambda] 处理进度:0%
[Lambda] 处理进度:20%
[Lambda] 处理进度:40%
[Lambda] 处理进度:60%
[Lambda] 处理进度:80%
[Lambda] 处理进度:100%
[Lambda] 数据处理完成:处理结果:新数据 456
6.7 匿名方法 vs Lambda:
[Lambda] 收到数据:比较数据
[匿名方法] 数据:比较数据
[Lambda] 数据:比较数据
6.8 自定义事件参数:
传感器读数变化:0 -> 25.5
变化时间:2026/3/16 13:25:20
传感器读数变化:25.5 -> 26
变化时间:2026/3/16 13:25:20
传感器读数变化:26 -> 25.8
变化时间:2026/3/16 13:25:20
七.异步 Lambda
7.1核心概念:
使用async和await关键字定义的Lambda,用于表示异步操作。其返回类型通常是Task或Task。
async关键字:声明与“赋能”
作用:它用于修饰一个方法或Lambda表达式,将其标记为一个“异步方法”。这是使用 await关键字的前提条件。它告诉编译器:“我这个方法内部将使用 await来进行异步等待,请为我生成必要的状态机代码来处理方法的暂停和恢复。”
await关键字:等待与“让权”
作用:它用于等待一个异步操作(通常是 Task或 Task<T>)完成。这是实现“异步”行为的核心操作。它是实际执行异步等待的指令。await只能出现在用 async修饰的方法或Lambda内部。
7.2语法结构:
async (参数) => { await 异步操作; … }
7.3详细说明与示例:
a.异步Lambda声明:
示例:Func> fetchDataAsync = async (id) => { await Task.Delay(100); return $“数据-{id}”; };
特性解析:Lambda前加async关键字,内部使用await等待异步操作。其类型是返回Task的Func委托。
b.异步事件处理器:
示例:asyncProcessor.ProcessingStarted += async (sender, fileName) => { await Task.Delay(50); … };
特性解析:事件类型可以是Func<object, T, Task>,允许用异步Lambda作为事件处理程序。
c.在LINQ Select中模拟异步转换:
示例:numbers.Select(async n => { await Task.Delay(1000); return n * n; });
特性解析:Select内的Lambda可以是异步的,返回Task。之后需通过Task.WhenAll或异步流(IAsyncEnumerable)来处理结果。
整个 Select操作的结果是一个 IEnumerable<Task<int>>,即一个包含10个“已启动但尚未完成”的 Task的集合。此时,所有模拟的“异步计算”都已经在后台运行了。
由于 IEnumerable 的延迟执行特性,Select 只是定义了转换规则,真正的数据处理(如遍历、计算、转换)会延迟到实际需要消费数据的那一刻(即首次对序列进行枚举时)才会发生。
d.协作式取消令牌:
示例:
using (var cts = new System.Threading.CancellationTokenSource())
{
CancellationToken cancellationToken = cts.Token;
try
{
await Task.Run(async () =>
{
cancellationToken.ThrowIfCancellationRequested();
...
}, cancellationToken); // 传递取消令牌
}
}
特性解析:
其原理基于两个核心类的协作:CancellationTokenSource(取消请求的发起者)和 CancellationToken(取消信号的传递载体)。整个过程是“请求-检测-响应”的协作式模型,而非强制中断线程。
事件的顺序:
T=0ms: 任务启动,进入第一次循环,打印处理中... 1,然后 await Task.Delay(100)。
T=100ms: 第一次 Task.Delay完成,进入第二次循环,打印处理中... 2,再次开始 await Task.Delay(100)。
T=200ms: 第二次 Task.Delay完成,进入第三次循环,打印处理中... 3,开始第三次 Task.Delay(100)。
T=250ms: 定时器触发!cts.Cancel()被调用,cts.Token的状态变为“已取消”。
T=300ms: 第三次 Task.Delay完成。当代码尝试继续执行,回到循环顶部,执行 cts.Token.ThrowIfCancellationRequested()时,该方法检测到令牌已取消,立即抛出 OperationCanceledException。
循环被强行中断,任务因异常而结束。异常被外层 catch块捕获,打印操作被取消。
7.4例程
#region 7. 异步 Lambda
/// <summary>
/// 演示异步 Lambda 表达式的使用
/// async: 标记方法为异步
/// await: 等待异步操作完成
/// Task: 表示异步操作的返回类型
/// </summary>
/// <returns>异步任务(Task)</returns>
static async Task DemonstrateAsyncLambda()
{
Console.WriteLine();
Console.WriteLine("=== 7. 异步 Lambda ===");
Console.WriteLine();
// 7.1 异步 Lambda 表达式
Console.WriteLine("7.1 异步 Lambda 表达式:");
// async (id) => { ... }: 异步 Lambda
// async: 使 Lambda 成为异步方法
// (id): Lambda 参数
// Task<string>: 返回类型
Func<int, Task<string>> fetchDataAsync = async (id) =>
{
Console.WriteLine($" 开始获取数据 {id}...");
await Task.Delay(100); // 异步等待 100ms
// await: 暂停执行直到 Task 完成,不阻塞线程,控制权返回给调用者
//大约100毫秒后,Task.Delay完成。系统会(可能在原线程,也可能在新线程)恢复执行 fetchDataAsync剩余的代码
Console.WriteLine($" 数据 {id} 获取完成");
return $"数据-{id}"; // 返回结果
};
// 调用异步 Lambda
// await: 等待异步操作完成
string result1 = await fetchDataAsync(1);
string result2 = await fetchDataAsync(2);
Console.WriteLine($" 结果:{result1}, {result2}");
// 7.2 并行异步操作
Console.WriteLine();
Console.WriteLine("7.2 并行异步操作:");
var tasks = new List<Task<string>>();
for (int i = 1; i <= 5; i++)
{
int id = i; // 创建局部变量副本,避免闭包问题
// Task.Run: 在线程池中运行异步操作
// 启动任务,但不立即await。此时所有Task几乎同时开始运行。
tasks.Add(Task.Run(async () =>
{
await Task.Delay(id * 100); // 模拟不同耗时的操作
return $"结果-{id}";
}));
}
// Task.WhenAll: 同时等待多个异步任务全部完成。它接收一个任务集合(如 Task[]或 Task<T>[]),并返回一个新的聚合任务。
var results = await Task.WhenAll(tasks);
Console.WriteLine($" 并行结果:{string.Join(", ", results)}");
// 7.3 异步事件处理器
Console.WriteLine();
Console.WriteLine("7.3 异步事件处理器:");
var asyncProcessor = new AsyncProcessor();
// 异步事件处理器 - Lambda 中使用 async/await
asyncProcessor.ProcessingStarted += async (sender, fileName) =>
{
Console.WriteLine($" 开始处理:{fileName}");
await Task.Delay(50);
Console.WriteLine($" 预处理完成:{fileName}");
};
asyncProcessor.ProcessingCompleted += async (sender, result) =>
{
Console.WriteLine($" 处理结果:{result}");
await Task.Delay(50);
Console.WriteLine($" 结果已记录");
};
await asyncProcessor.ProcessFileAsync("data.csv");
// 7.4 异步 LINQ
Console.WriteLine();
Console.WriteLine("7.4 异步 LINQ:");
var numbers = Enumerable.Range(1, 10);
// 异步 Select - 每个元素的转换都是异步的
// async n => { ... }: 异步 Lambda
// 整个 Select操作的结果是一个 IEnumerable<Task<int>>,即一个包含10个“已启动但尚未完成”的 Task的集合。此时,所有模拟的“异步计算”都已经在后台运行了。
// 由于 IEnumerable 的延迟执行特性,Select 只是定义了转换规则,真正的数据处理(如遍历、计算、转换)会延迟到实际需要消费数据的那一刻(即首次对序列进行枚举时)才会发生。
var asyncSquares = numbers.Select(async n =>
{
await Task.Delay(1000);
return n * n;
}).ToList(); // 强制立即枚举,启动所有 Task
Thread.Sleep(1000);
Console.WriteLine(" 异步计算平方:");
// await foreach: 遍历执行异步 enumerable
await foreach (var square in ToAsyncEnumerable(asyncSquares))
{
Console.WriteLine($" {square}");//在这里取结果前应该已经计算完成,一下子全部打印出来
}
// 7.5 取消令牌
Console.WriteLine();
Console.WriteLine("7.5 取消令牌:");
using (var cts = new System.Threading.CancellationTokenSource())
{
// CancelAfter: 指定时间后自动取消
cts.CancelAfter(250); // 250ms 后取消
// 获取取消令牌 (CancellationToken) - 信号载体
CancellationToken cancellationToken = cts.Token;
try
{
await Task.Run(async () =>
{
for (int i = 0; i < 10; i++)
{
// ThrowIfCancellationRequested: 检查是否被取消
cancellationToken.ThrowIfCancellationRequested();
Console.WriteLine($" 处理中... {i + 1}");
await Task.Delay(100);
}
}, cancellationToken); // 传递取消令牌
}
catch (OperationCanceledException)
{
Console.WriteLine(" 操作被取消");
}
}
}
/// <summary>
/// 辅助方法:将 Task 集合转换为异步枚举
/// IAsyncEnumerable<T>: 异步可枚举接口
/// yield return: 迭代器返回
/// </summary>
/// <typeparam name="T">元素类型</typeparam>
/// <param name="tasks">任务集合</param>
/// <returns>异步枚举器</returns>
static async IAsyncEnumerable<T> ToAsyncEnumerable<T>(IEnumerable<Task<T>> tasks)
{
foreach (var task in tasks)
{
//yield return:它告诉编译器,当调用方迭代此方法返回的序列时,每次遇到 yield return,就立即将后面的表达式(await task的结果)作为当前元素返回给调用方,并暂停当前方法的执行,保存所有局部状态。
//下次迭代时,方法会从上次暂停的地方(yield return语句之后)直接恢复执行,继续循环,处理下一个任务,直到没有更多元素。
yield return await task; // 逐个返回完成的任务结果
}
}
#endregion
7.5输出
=== 7. 异步 Lambda ===
7.1 异步 Lambda 表达式:
开始获取数据 1...
数据 1 获取完成
开始获取数据 2...
数据 2 获取完成
结果:数据-1, 数据-2
7.2 并行异步操作:
并行结果:结果-1, 结果-2, 结果-3, 结果-4, 结果-5
7.3 异步事件处理器:
开始处理:data.csv
预处理完成:data.csv
处理结果:data.csv 处理完成,大小:1024KB
结果已记录
7.4 异步 LINQ:
异步计算平方:
1
4
9
16
25
36
49
64
81
100
7.5 取消令牌:
处理中... 1
处理中... 2
处理中... 3
操作被取消
八.高级用法
8.1核心概念:
介绍函数式编程风格的高级Lambda用法。
8.2详细说明与示例:
a.递归Lambda:
示例:
Func? factorial = null;
factorial = n => n <= 1 ? 1 : n * factorial!(n - 1);
特性解析:Lambda需先声明为可空变量,再在定义中通过factorial!调用自身(使用非空断言!)。
b.高阶函数(函数作为输入输出):
示例:Func> multiplierFactory = factor => x => x * factor;
特性解析:multiplierFactory是一个返回Lambda的Lambda(高阶函数)。factor被内层Lambda捕获,实现了“工厂”模式。
c.柯里化:
示例:Func> addCurried = a => b => c => a + b + c;
特性解析:将多参数函数转换为一系列单参数函数的链式调用。addCurried(5)(3)(2)等价于(5,3,2)=>5+3+2。
d.函数组合:
示例:Func composed = addOne.Compose(multiplyByTwo).Compose(square2);
特性解析:通过自定义的Compose扩展方法,将多个Lambda(函数)组合成一个新的函数,执行顺序是从左到右。
e.记忆化:
示例:if (memo.TryGetValue(n, out var value)) return value;
特性解析:记忆化示例巧妙地将Lambda的闭包特性(用于维持共享的缓存状态)与递归能力(实现算法逻辑)结合在一起。闭包提供了跨调用持久化“记忆”的能力,而递归Lambda则利用这份“记忆”来避免重复计算,二者协同实现了性能优化。这一切都建立在Lambda可以作为委托实例灵活使用的特性之上。
f.模式匹配:
示例:Func<object, string> describe = obj => obj switch{...};
特性解析:switch表达式是 C# 8.0 引入的增强模式匹配语法,它对一个输入表达式 (obj) 进行匹配,并根据匹配到的模式返回一个结果值。整个表达式本身会产出一个值,可以用于赋值或返回。相比传统的 if-else或 switch语句,switch表达式语法更简洁、声明式,能直接返回值,并且通过模式匹配提供了更强大的类型检查和提取能力。
8.3例程
#region 8. 高级主题
/// <summary>
/// 演示 Lambda 的高级用法和函数式编程概念
/// </summary>
static void DemonstrateAdvancedTopics()
{
Console.WriteLine();
Console.WriteLine("=== 8. 高级主题 ===");
Console.WriteLine();
// 8.1 递归 Lambda
Console.WriteLine("8.1 递归 Lambda:");
// 递归 Lambda 需要特殊处理
// Func<int, int>?: 可空的委托类型
Func<int, int>? factorial = null;
// 先声明变量,再赋值,以便在 Lambda 内部引用自身
factorial = n => n <= 1 ? 1 : n * factorial!(n - 1);
// n: 输入参数
// n <= 1 ? 1 : ...: 三元运算符,基本情况
// factorial!(n - 1): 递归调用,! 是非空断言
// 因为编译器不知道 factorial 在此时一定不为 null
Console.WriteLine($" 阶乘 5! = {factorial(5)}"); // 120
Console.WriteLine($" 阶乘 10! = {factorial(10)}"); // 3628800
// 8.2 Lambda 作为返回值(工厂模式)
Console.WriteLine();
Console.WriteLine("8.2 Lambda 作为返回值:");
// 高阶函数:返回函数的函数
// factor: 外层 Lambda 参数
// x => x * factor: 内层 Lambda,捕获 factor
Func<int, Func<int, int>> multiplierFactory = factor => x => x * factor;
// Func<int, Func<int, int>>:
// 第一个 int: factor 参数类型
// Func<int, int>: 返回的 Lambda 类型
var doubleIt = multiplierFactory(2); // 创建乘以 2 的函数
// doubleIt 等价于:x => x * 2
var tripleIt = multiplierFactory(3); // 创建乘以 3 的函数
// tripleIt 等价于:x => x * 3
Console.WriteLine($" doubleIt(5) = {doubleIt(5)}"); // 10
Console.WriteLine($" tripleIt(5) = {tripleIt(5)}"); // 15
// 8.3 柯里化(Currying)
Console.WriteLine();
Console.WriteLine("8.3 柯里化:");
// 柯里化:将多参数函数转换为一系列单参数函数
// a => b => c => a + b + c
// 读作:a => (b => (c => a + b + c))
Func<int, Func<int, Func<int, int>>> addCurried = a => b => c => a + b + c;
var add5 = addCurried(5); // 固定第一个参数为 5,返回 b => c => 5 + b + c
var add5And3 = add5(3); // 固定第二个参数为 3,返回 c => 5 + 3 + c
var result = add5And3(2); // 传入第三个参数,返回 5 + 3 + 2 = 10
Console.WriteLine($" 柯里化加法:5 + 3 + 2 = {result}");
Console.WriteLine($" 直接调用:{addCurried(5)(3)(2)}");
// 8.4 组合函数(Function Composition)
Console.WriteLine();
Console.WriteLine("8.4 组合函数:");
// 定义基础函数
Func<int, int> addOne = x => x + 1; // f(x) = x + 1
Func<int, int> multiplyByTwo = x => x * 2; // g(x) = x * 2
Func<int, int> square2 = x => x * x; // h(x) = x²
// 手动组合:h(g(f(x)))
// x => multiplyByTwo(addOne(x)): 先加 1,再乘 2
Func<int, int> addThenMultiply = x => multiplyByTwo(addOne(x));
// 使用扩展方法组合
// addOne.Compose(multiplyByTwo): 先执行 addOne,再执行 multiplyByTwo
// .Compose(square2): 再执行 square2
Func<int, int> composed = addOne.Compose(multiplyByTwo).Compose(square2);
// 最终效果:((x + 1) * 2)²
Console.WriteLine($" addThenMultiply(3) = {addThenMultiply(3)}"); // (3+1)*2 = 8
Console.WriteLine($" composed(3) = {composed(3)}"); // ((3+1)*2)² = 64
// 8.5 记忆化(Memoization)
Console.WriteLine();
Console.WriteLine("8.5 记忆化:");
// 带记忆化的斐波那契数列
Func<long, long>? fibonacci = null;
var memo = new Dictionary<long, long>(); // 缓存字典
fibonacci = n =>
{
// TryGetValue: 尝试获取缓存值
// n: 键
// out var value: 输出的值
// 如果存在缓存,直接返回,避免重复计算
if (memo.TryGetValue(n, out var value)) return value;
if (n <= 2) return 1; // 基本情况:F(1)=1, F(2)=1
// 递归计算并缓存
var result = fibonacci!(n - 1) + fibonacci!(n - 2);
memo[n] = result; // 存入缓存
return result;
};
Console.WriteLine($" 斐波那契数列:");
for (int i = 1; i <= 10; i++)
{
Console.WriteLine($" F({i}) = {fibonacci(i)}");
}
// 8.6 模式匹配(Pattern Matching)
Console.WriteLine();
Console.WriteLine("8.6 模式匹配:");
// switch 表达式(C# 8.0+)
// obj switch { ... }: 对 obj 进行模式匹配
Func<object, string> describe = obj => obj switch
{
int i when i > 0 => $"正整数:{i}", // 类型模式 + 条件
int i when i < 0 => $"负整数:{i}", // 类型模式 + 条件
int i => $"零:{i}", // 类型模式
string s => $"字符串:'{s}'", // 类型模式
null => "空值", // 常量模式
_ => $"其他类型:{obj.GetType().Name}" // 丢弃模式(默认情况)
};
// _: 丢弃模式,匹配任何其他情况
Console.WriteLine($" describe(42) = {describe(42)}");
Console.WriteLine($" describe(-5) = {describe(-5)}");
Console.WriteLine($" describe(0) = {describe(0)}");
Console.WriteLine($" describe(\"Hello\") = {describe("Hello")}");
Console.WriteLine($" describe(3.14) = {describe(3.14)}");
}
#endregion
8.4输出
=== 8. 高级主题 ===
8.1 递归 Lambda:
阶乘 5! = 120
阶乘 10! = 3628800
8.2 Lambda 作为返回值:
doubleIt(5) = 10
tripleIt(5) = 15
8.3 柯里化:
柯里化加法:5 + 3 + 2 = 10
直接调用:10
8.4 组合函数:
addThenMultiply(3) = 8
composed(3) = 64
8.5 记忆化:
斐波那契数列:
F(1) = 1
F(2) = 1
F(3) = 2
F(4) = 3
F(5) = 5
F(6) = 8
F(7) = 13
F(8) = 21
F(9) = 34
F(10) = 55
8.6 模式匹配:
describe(42) = 正整数:42
describe(-5) = 负整数:-5
describe(0) = 零:0
describe("Hello") = 字符串:'Hello'
describe(3.14) = 其他类型:Double
九.实际应用示例
9.1核心概念:
展示Lambda在具体设计模式和框架中的应用。
9.2详细说明与示例:
a.配置系统(流畅调用API):
示例:config.Configure(options => { options.LogLevel = “Debug”; … });
Lambda特性:Lambda作为委托参数(回调/配置器)。
特性解析:
Configure方法接受一个 Action<AppConfig.Options>类型的委托作为参数。调用方通过传入一个Lambda表达式 options => { ... }来具体实现这个委托。
这个Lambda充当了一个配置器的角色,它接收一个配置对象 options作为输入,并在其语句块中对其进行修改。这种方式将配置逻辑内联、即时地定义在调用处,代码意图非常清晰,无需预先定义额外的方法,形成了流畅的API调用风格。
b.验证器(规则引擎):
示例:validator.AddRule(u => !string.IsNullOrEmpty(u.Name), “姓名不能为空”);
所用Lambda特性:Lambda作为谓词(判断条件) 与 组合使用(多规则集合)
特性解析:
每个验证规则都是一个 Func<User, bool>类型的Lambda,例如u=>!string.IsNullOrEmpty(u.Name)。这是一个谓词,它接受一个用户对象,返回一个布尔值表示该规则是否通过。
Validator<T>类内部维护了一个这样的Lambda委托列表。通过 AddRule方法,可以灵活地添加任意数量的验证规则,每个规则都是一个独立的Lambda。这体现了Lambda的轻量级和可组合性,使得验证规则可以像数据一样被动态管理和执行。

c.中间件管道(责任链模式):
示例:pipeline.Use(async (context, next) => { … await next(); … });
Lambda特性:异步Lambda 与 Lambda捕获外部变量(闭包)
特性解析:
异步Lambda:每个中间件都是一个 Func<PipelineContext, Func<Task>, Task>类型的异步Lambda。Lambda内部使用了 async/await关键字(如 await next();)来非阻塞地调用管道中的下一个中间件,并可以在调用前后执行逻辑。
闭包:每个Lambda都捕获了其外部的 PipelineContext context和 Func<Task> next参数。这使得它们能够操作共享的上下文 (context.Data += "->1") 并控制流程的传递 (await next())。通过将多个这样的Lambda按顺序添加到管道中,就构建了一条可扩展的请求处理链。
执行开始
│
▼
[Middleware 1] 开始
│
▼
context.Data = "请求->1" (修改数据)
│
▼
await next() ──────────────┐
│ │
│ ▼
│ [Middleware 2] 开始
│ │
│ ▼
│ context.Data = "请求->1->2"
│ │
│ ▼
│ await next() ───────┐
│ │ │
│ │ ▼
│ │ [Middleware 3] 开始
│ │ │
│ │ ▼
│ │ context.Data = "请求->1->2->3"
│ │ │
│ │ ▼
│ │ await next() (无下一个中间件)
│ │ │
│ │ ▼
│ │ [Middleware 3] 结束
│ │ │
│ │ │
│ ◄───────────┘
│ │
│ ▼
│ [Middleware 2] 结束
│ │
│ │
◄──────────────────────┘
│
▼
[Middleware 1] 结束
│
▼
执行完成
最终数据:"请求->1->2->3"
d.策略模式:
示例:calculator.AddStrategy(“VIP”, amount => amount * 0.8m);
Lambda特性:Lambda作为轻量级的策略实现
特性解析:
每个折扣策略(如VIP折扣、学生折扣)都被定义为一个 Func<decimal, decimal>类型的Lambda,例如 amount => amount * 0.8m。这个Lambda完整封装了该策略的算法逻辑。
DiscountCalculator类内部使用一个字典来存储策略名称与对应Lambda委托的映射。当需要计算折扣时,根据策略名从字典中取出对应的Lambda并执行。这避免了为每个策略创建单独的类,利用Lambda的简洁语法和第一类函数的特性,将策略直接表示为可执行的代码块,极大地减少了样板代码。
9.3例程
#region 9. 实际应用示例
/// <summary>
/// 演示 Lambda 在实际开发中的应用场景
/// </summary>
static void DemonstratePracticalExamples()
{
Console.WriteLine();
Console.WriteLine("=== 9. 实际应用示例 ===");
Console.WriteLine();
// 9.1 配置系统(流畅调用API)
Console.WriteLine("9.1 配置系统:");
var config = new AppConfig();
// 使用 Lambda 配置选项
// options: 配置对象
// { ... }: 语句块,设置属性
config.Configure(options =>
{
options.LogLevel = "Debug";
options.Timeout = TimeSpan.FromSeconds(30);
options.Features = new List<string> { "Auth", "Caching", "Logging" };
});
config.PrintConfig();
// 9.2 验证器(规则引擎)
Console.WriteLine();
Console.WriteLine("9.2 验证器:");
var userValidator = new Validator<User>();
// 添加验证规则 - 每个规则是一个 Lambda
// u => !string.IsNullOrEmpty(u.Name): 验证姓名非空
userValidator.AddRule(u => !string.IsNullOrEmpty(u.Name), "姓名不能为空");
// u => u.Age >= 0 && u.Age <= 150: 验证年龄范围
userValidator.AddRule(u => u.Age >= 0 && u.Age <= 150, "年龄必须在 0-150 之间");
// u => u.Email?.Contains('@') ?? false: 验证邮箱格式
// ?. : 空条件运算符,如果 u.Email 为 null 则返回 null
// ??: 空合并运算符,如果左边为 null 则返回 false
userValidator.AddRule(u => u.Email?.Contains('@') ?? false, "邮箱格式不正确");
// 创建测试用户
var testUser = new User
{
Name = "John",
Age = 200, // 无效年龄
Email = "invalid-email"
};
// 执行验证
var validationResult = userValidator.Validate(testUser);
Console.WriteLine(" 用户验证结果:");
foreach (var error in validationResult.Errors)
{
Console.WriteLine($" - {error}");
}
// 9.3 中间件管道(责任链模式)
Console.WriteLine();
Console.WriteLine("9.3 中间件管道:");
var pipeline = new MiddlewarePipeline();
// 添加中间件 1
// (context, next): 中间件的两个参数
// context: 管道上下文,携带请求数据
// next: 下一个中间件的委托
pipeline.Use(async (context, next) =>
{
Console.WriteLine(" [Middleware 1] 开始");
context.Data += "->1"; // 处理数据
await next(); // 调用下一个中间件
Console.WriteLine(" [Middleware 1] 结束");
});
// 添加中间件 2
pipeline.Use(async (context, next) =>
{
Console.WriteLine(" [Middleware 2] 开始");
context.Data += "->2";
await next();
Console.WriteLine(" [Middleware 2] 结束");
});
// 添加中间件 3
pipeline.Use(async (context, next) =>
{
Console.WriteLine(" [Middleware 3] 开始");
context.Data += "->3";
await next();
Console.WriteLine(" [Middleware 3] 结束");
});
// 执行管道
var context = new PipelineContext { Data = "请求" };
pipeline.Execute(context).Wait(); // 同步等待
Console.WriteLine($" 最终数据:{context.Data}");
// 9.4 策略模式
Console.WriteLine();
Console.WriteLine("9.4 策略模式:");
var calculator = new DiscountCalculator();
// 添加折扣策略 - 每个策略是一个 Lambda
// amount => amount * 0.8m: 8 折
// decimal 字面量使用 m 后缀
calculator.AddStrategy("VIP", amount => amount * 0.8m);
// amount => amount * 0.9m: 9 折
calculator.AddStrategy("Student", amount => amount * 0.9m);
// amount => amount > 100 ? amount - 20 : amount * 0.8m
// 三元运算符:满 100 减 20,否则 8 折
calculator.AddStrategy("Double11", amount =>
amount > 100 ? amount - 20 : amount * 0.8m);
Console.WriteLine(" 折扣计算:");
Console.WriteLine($" VIP 100 元 -> {calculator.Calculate("VIP", 100m):C}");
Console.WriteLine($" Student 100 元 -> {calculator.Calculate("Student", 100m):C}");
Console.WriteLine($" Double11 150 元 -> {calculator.Calculate("Double11", 150m):C}");
Console.WriteLine($" Double11 80 元 -> {calculator.Calculate("Double11", 80m):C}");
}
#endregion
9.4输出
=== 9. 实际应用示例 ===
9.1 配置系统:
应用配置:
日志级别:Debug
超时时间:30秒
功能列表:Auth, Caching, Logging
9.2 验证器:
用户验证结果:
- 年龄必须在 0-150 之间
- 邮箱格式不正确
9.3 中间件管道:
[Middleware 3] 开始
[Middleware 2] 开始
[Middleware 1] 开始
[Middleware 1] 结束
[Middleware 2] 结束
[Middleware 3] 结束
最终数据:请求->3->2->1
9.4 策略模式:
折扣计算:
VIP 100 元 -> ¥80.00
Student 100 元 -> ¥90.00
Double11 150 元 -> ¥130.00
Double11 80 元 -> ¥64.00
十.程序主体
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using System.Reflection;
using System.Diagnostics;
using System.ComponentModel;
#pragma warning disable IDE0039//抑制局部函数优于Lambda警告
#pragma warning disable IDE0063//抑制简化using警告
#pragma warning disable IDE0090//抑制简化new警告
#pragma warning disable IDE0028//抑制简化集合初始化警告
#pragma warning disable IDE0300//抑制简化数组初始化警告
#pragma warning disable IDE0350//抑制简化Lambda警告
#pragma warning disable IDE0290//抑制主构造函数警告
namespace CSharpLambdaCompleteDemo
{
/// <summary>
/// 主程序类 - 完整演示 C# Lambda 表达式的语法和高级应用
/// Lambda 表达式是一种匿名函数,可用于创建委托或表达式树对象
/// </summary>
class Program
{
下面各个章节单独列举:
1. 基本 Lambda 语法
2. 委托与 Lambda
3. 闭包与变量捕获
4. LINQ 与 Lambda
5. 表达式树 (Expression Trees)
6. 事件与 Lambda
7. 异步 Lambda
8. 高级主题
9. 实际应用示例
/// <summary>
/// 程序入口点(Main 方法)
/// async Task: 异步 Main 方法(C# 7.1+)
/// </summary>
/// <param name="args">命令行参数</param>
static async Task Main(string[] args)
{
Console.WriteLine("C# Lambda 表达式完整演示");
Console.WriteLine("=============================");
Console.WriteLine();
// 依次执行各个演示模块
DemonstrateBasicSyntax();
DemonstrateDelegatesAndLambda();
DemonstrateClosures();
DemonstrateLINQAndLambda();
DemonstrateExpressionTrees();
DemonstrateEventsAndLambda();
await DemonstrateAsyncLambda(); // 异步方法需要 await
DemonstrateAdvancedTopics();
DemonstratePracticalExamples();
Console.WriteLine();
Console.WriteLine("演示结束,按任意键退出...");
Console.ReadKey();
}
}
#region 辅助类定义
/// <summary>
/// 人员类(基础数据模型)
/// </summary>
class Person
{
/// <summary>
/// 姓名属性
/// string?: 可空引用类型(允许为 null)
/// get; set;: 自动实现的属性
/// </summary>
public string? Name { get; set; }
/// <summary>
/// 年龄属性
/// int: 值类型(不允许为 null)
/// </summary>
public int Age { get; set; }
}
/// <summary>
/// 员工类(用于 LINQ 演示的数据模型)
/// </summary>
class Employee
{
/// <summary>
/// 员工 ID(主键)
/// </summary>
public int Id { get; set; }
/// <summary>
/// 姓名
/// </summary>
public string? Name { get; set; }
/// <summary>
/// 部门名称
/// </summary>
public string? Department { get; set; }
/// <summary>
/// 工资(double 类型)
/// </summary>
public double Salary { get; set; }
/// <summary>
/// 年龄
/// </summary>
public int Age { get; set; }
}
/// <summary>
/// 项目类(用于演示 Join 操作)
/// </summary>
class Project
{
/// <summary>
/// 项目 ID(主键)
/// </summary>
public int Id { get; set; }
/// <summary>
/// 项目名称
/// </summary>
public string? Name { get; set; }
/// <summary>
/// 负责人 ID(外键,关联 Employee.Id)
/// </summary>
public int LeaderId { get; set; }
}
/// <summary>
/// 数据处理器类(用于演示事件处理)
/// </summary>
class DataProcessor
{
/// <summary>
/// 数据接收事件
/// event: 事件关键字(封装委托,只能+= 和-=)
/// EventHandler<string>: 泛型事件处理器委托
/// ?: 可空引用类型
/// </summary>
public event EventHandler<string>? DataReceived;
/// <summary>
/// 数据处理完成事件
/// EventHandler<ProcessingEventArgs>: 携带自定义事件参数
/// </summary>
public event EventHandler<ProcessingEventArgs>? DataProcessed;
/// <summary>
/// 进度变更事件
/// EventHandler<int>: 携带 int 类型的进度值
/// </summary>
public event EventHandler<int>? ProgressChanged;
/// <summary>
/// 开始处理数据(触发一系列事件)
/// </summary>
/// <param name="data">要处理的数据</param>
public void StartProcessing(string data)
{
OnDataReceived(data); // 触发数据接收事件
// 模拟处理进度(0% 到 100%)
for (int i = 0; i <= 100; i += 10)
{
OnProgressChanged(i); // 触发进度事件
System.Threading.Thread.Sleep(50); // 模拟耗时
}
var result = $"处理结果:{data.ToUpper()}";
OnDataProcessed(result); // 触发处理完成事件
}
/// <summary>
/// 处理单个数据(仅触发接收事件)
/// </summary>
/// <param name="data">数据</param>
public void ProcessSingleData(string data)
{
OnDataReceived(data);
}
/// <summary>
/// 触发数据接收事件(受保护的虚方法)
/// virtual: 可被子类重写
/// </summary>
/// <param name="data">数据</param>
protected virtual void OnDataReceived(string data)
{
// ?: 空条件运算符,如果 DataReceived 不为 null 才调用
DataReceived?.Invoke(this, data);
}
/// <summary>
/// 触发数据处理完成事件
/// </summary>
/// <param name="result">处理结果</param>
protected virtual void OnDataProcessed(string result)
{
DataProcessed?.Invoke(this, new ProcessingEventArgs(result));
}
/// <summary>
/// 触发进度变更事件
/// </summary>
/// <param name="progress">进度百分比(0-100)</param>
protected virtual void OnProgressChanged(int progress)
{
ProgressChanged?.Invoke(this, progress);
}
}
/// <summary>
/// 处理事件参数类(携带处理结果信息)
/// 继承自 EventArgs(事件参数的基类)
/// </summary>
class ProcessingEventArgs : EventArgs
{
/// <summary>
/// 处理结果(只读属性)
/// get;: 只有 getter,外部不可修改
/// </summary>
public string Result { get; }
/// <summary>
/// 构造函数(初始化事件参数)
/// </summary>
/// <param name="result">处理结果</param>
public ProcessingEventArgs(string result)
{
Result = result;
}
}
/// <summary>
/// 传感器类(演示属性变更事件)
/// </summary>
class Sensor
{
private double _reading; // 私有字段,存储实际值
/// <summary>
/// 传感器读数属性(带变更检测)
/// get => _reading: 表达式-bodied getter
/// set: setter,包含变更检测和事件触发
/// </summary>
public double Reading
{
get => _reading; // 返回字段值
set
{
// 检查变化是否超过阈值(避免微小变化触发事件)
if (Math.Abs(_reading - value) > 0.1)
{
var oldValue = _reading; // 保存旧值
_reading = value; // 更新字段
OnReadingChanged(oldValue, value); // 触发事件
}
}
}
/// <summary>
/// 读数变更事件
/// </summary>
public event EventHandler<ReadingChangedEventArgs>? ReadingChanged;
/// <summary>
/// 触发读数变更事件(受保护的虚方法)
/// </summary>
/// <param name="oldValue">旧值</param>
/// <param name="newValue">新值</param>
protected virtual void OnReadingChanged(double oldValue, double newValue)
{
ReadingChanged?.Invoke(this, new ReadingChangedEventArgs(oldValue, newValue));
}
}
/// <summary>
/// 读数变更事件参数类(携带读数变化的详细信息)
/// </summary>
class ReadingChangedEventArgs : EventArgs
{
/// <summary>
/// 旧读数(只读)
/// </summary>
public double OldValue { get; }
/// <summary>
/// 新读数(只读)
/// </summary>
public double NewValue { get; }
/// <summary>
/// 变更时间(只读)
/// </summary>
public DateTime ChangeTime { get; }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="oldValue">旧值</param>
/// <param name="newValue">新值</param>
public ReadingChangedEventArgs(double oldValue, double newValue)
{
OldValue = oldValue;
NewValue = newValue;
ChangeTime = DateTime.Now; // 记录当前时间
}
}
/// <summary>
/// 异步处理器类(演示异步事件处理)
/// </summary>
class AsyncProcessor
{
/// <summary>
/// 处理开始事件(异步版本)
/// Func<object, string, Task>: 返回 Task 的委托(异步)
/// object: sender 参数
/// string: fileName 参数
/// Task: 返回类型(异步操作)
/// </summary>
public event Func<object, string, Task>? ProcessingStarted;
/// <summary>
/// 处理完成事件(异步版本)
/// </summary>
public event Func<object, string, Task>? ProcessingCompleted;
/// <summary>
/// 异步处理文件
/// async Task: 异步方法
/// </summary>
/// <param name="fileName">文件名</param>
/// <returns>异步任务</returns>
public async Task ProcessFileAsync(string fileName)
{
// 触发处理开始事件
if (ProcessingStarted != null)
{
await ProcessingStarted.Invoke(this, fileName);
}
await Task.Delay(200); // 模拟处理过程
var result = $"{fileName} 处理完成,大小:1024KB";
// 触发处理完成事件
if (ProcessingCompleted != null)
{
await ProcessingCompleted.Invoke(this, result);
}
}
}
/// <summary>
/// 应用配置类(演示使用 Lambda 进行配置)
/// </summary>
class AppConfig
{
/// <summary>
/// 配置选项类(嵌套类)
/// </summary>
public class Options
{
/// <summary>
/// 日志级别属性
/// </summary>
public string LogLevel { get; set; } = "Info";
/// <summary>
/// 超时时间属性
/// TimeSpan: 时间跨度类型
/// </summary>
public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(10);
/// <summary>
/// 功能列表属性
/// List<string>: 字符串列表
/// </summary>
public List<string> Features { get; set; } = new List<string>();
}
private readonly Options _options = new Options(); // 配置对象
/// <summary>
/// 配置方法(接受 Action 委托)
/// Action<Options>: 接受 Options 参数,无返回值
/// configure?: 可空委托
/// </summary>
/// <param name="configure">配置委托(Lambda)</param>
public void Configure(Action<Options> configure)
{
configure?.Invoke(_options); // 调用配置 Lambda
}
/// <summary>
/// 打印配置信息到控制台
/// </summary>
public void PrintConfig()
{
Console.WriteLine(" 应用配置:");
Console.WriteLine($" 日志级别:{_options.LogLevel}");
Console.WriteLine($" 超时时间:{_options.Timeout.TotalSeconds}秒");
Console.WriteLine($" 功能列表:{string.Join(", ", _options.Features)}");
}
}
/// <summary>
/// 用户类(用于验证器演示)
/// </summary>
class User
{
/// <summary>
/// 姓名
/// </summary>
public string? Name { get; set; }
/// <summary>
/// 年龄
/// </summary>
public int Age { get; set; }
/// <summary>
/// 邮箱地址
/// </summary>
public string? Email { get; set; }
}
/// <summary>
/// 验证结果类(封装验证结果和错误信息)
/// </summary>
class ValidationResult
{
/// <summary>
/// 是否验证通过(计算属性)
/// => Errors.Count == 0: 表达式-bodied 属性
/// </summary>
public bool IsValid => Errors.Count == 0;
/// <summary>
/// 错误信息列表
/// { get; } = ...: 只读属性,初始化时赋值
/// </summary>
public List<string> Errors { get; } = new List<string>();
}
/// <summary>
/// 泛型验证器类(使用 Lambda 定义验证规则)
/// </summary>
/// <typeparam name="T">要验证的类型</typeparam>
class Validator<T>
{
// 规则列表:每个规则是一个 Func<T, bool> 委托
private readonly List<Func<T, bool>> _rules = new List<Func<T, bool>>();
// 错误消息列表(与规则一一对应)
private readonly List<string> _errorMessages = new List<string>();
/// <summary>
/// 添加验证规则
/// </summary>
/// <param name="rule">验证规则(Lambda 表达式)</param>
/// <param name="errorMessage">错误消息</param>
public void AddRule(Func<T, bool> rule, string errorMessage)
{
_rules.Add(rule); // 添加规则委托
_errorMessages.Add(errorMessage); // 添加对应的错误消息
}
/// <summary>
/// 执行验证
/// </summary>
/// <param name="item">要验证的对象</param>
/// <returns>验证结果对象</returns>
public ValidationResult Validate(T item)
{
var result = new ValidationResult(); // 创建结果对象
// 遍历所有规则
for (int i = 0; i < _rules.Count; i++)
{
// _rules[i](item): 调用第 i 个规则委托
// 如果不满足规则(返回 false),添加错误消息
if (!_rules[i](item))
{
result.Errors.Add(_errorMessages[i]);
}
}
return result;
}
}
/// <summary>
/// 管道上下文类(在中间件之间传递数据)
/// </summary>
class PipelineContext
{
/// <summary>
/// 数据属性(在中间件中传递和修改)
/// </summary>
public string? Data { get; set; }
}
/// <summary>
/// 请求委托(定义中间件的签名)
/// </summary>
/// <param name="context">管道上下文</param>
/// <returns>异步任务</returns>
delegate Task RequestDelegate(PipelineContext context);
/// <summary>
/// 中间件管道类(实现责任链模式)
/// </summary>
class MiddlewarePipeline
{
private RequestDelegate? _pipeline; // 管道委托(存储中间件链)
/// <summary>
/// 添加中间件到管道
/// </summary>
/// <param name="middleware">中间件委托</param>
public void Use(Func<PipelineContext, Func<Task>, Task> middleware)
{
// 如果_pipeline 为 null,使用默认的空操作
var next = _pipeline ?? (context => Task.CompletedTask);
// ??: 空合并运算符,如果左边为 null 则使用右边
// 构建新的管道:当前中间件包装下一个中间件
_pipeline = context => middleware(context, () => next(context));
// context => ...: 新的 RequestDelegate
// middleware(context, () => next(context)): 调用中间件
// () => next(context): 下一个中间件的包装
}
/// <summary>
/// 执行管道(运行所有中间件)
/// </summary>
/// <param name="context">管道上下文</param>
/// <returns>异步任务</returns>
public async Task Execute(PipelineContext context)
{
if (_pipeline != null)
{
await _pipeline(context); // 执行管道
}
}
}
/// <summary>
/// 折扣计算器类(实现策略模式)
/// </summary>
class DiscountCalculator
{
// 策略字典:键是策略名称,值是折扣计算委托
private readonly Dictionary<string, Func<decimal, decimal>> _strategies =
new Dictionary<string, Func<decimal, decimal>>();
/// <summary>
/// 添加折扣策略
/// </summary>
/// <param name="name">策略名称</param>
/// <param name="strategy">折扣计算委托(Lambda)</param>
public void AddStrategy(string name, Func<decimal, decimal> strategy)
{
_strategies[name] = strategy; // 添加到字典
}
/// <summary>
/// 计算折扣后价格
/// </summary>
/// <param name="strategyName">策略名称</param>
/// <param name="amount">原价金额</param>
/// <returns>折扣后价格</returns>
public decimal Calculate(string strategyName, decimal amount)
{
// TryGetValue: 尝试获取策略
if (_strategies.TryGetValue(strategyName, out var strategy))
{
return strategy(amount); // 应用策略(调用 Lambda)
}
return amount; // 默认无折扣
}
}
#endregion
#region 扩展方法
/// <summary>
/// Lambda 扩展方法类(静态工具类)
/// static: 静态类,不能被实例化
/// </summary>
static class LambdaExtensions
{
/// <summary>
/// 获取前 N 个元素(按指定键降序排列)
/// 扩展方法:使用 this 关键字
/// </summary>
/// <typeparam name="T">元素类型</typeparam>
/// <typeparam name="TKey">键类型</typeparam>
/// <param name="source">源集合(this 参数)</param>
/// <param name="keySelector">键选择器 Lambda</param>
/// <param name="n">要获取的数量</param>
/// <returns>前 N 个元素</returns>
public static IEnumerable<T> TopN<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector, int n)
{
// source: 被扩展的集合(this 参数)
// keySelector: Lambda 表达式,用于提取排序键
// OrderByDescending: 按降序排序
// Take: 取前 n 个元素
return source.OrderByDescending(keySelector).Take(n);
}
/// <summary>
/// 组合两个函数(函数式编程中的函数组合)
/// 数学定义:(g ∘ f)(x) = g(f(x))
/// </summary>
/// <typeparam name="T">输入和输出类型</typeparam>
/// <param name="func1">第一个函数 f</param>
/// <param name="func2">第二个函数 g</param>
/// <returns>组合后的函数</returns>
public static Func<T, T> Compose<T>(this Func<T, T> func1, Func<T, T> func2)
{
// x => func2(func1(x)): 组合函数
// 先执行 func1,再将结果传给 func2
return x => func2(func1(x));
}
}
#endregion
}
更多推荐

所有评论(0)