Skip to Content
Week 03Day 4 - 委托与 Lambda

Day 4 - 委托与 Lambda

建议用时:220-260 分钟

你将学会什么

  • 委托为什么存在,它解决什么问题
  • FuncActionPredicate 分别什么时候用
  • Lambda 的完整读法,不再只会照着写 =>
  • 如何把“判断规则”“计算规则”“输出动作”当参数传进去
  • 闭包是什么,为什么 Lambda 能记住外面的变量
  • 委托和 Lambda 在 LINQ、事件、回调里的真实用法

本页不要把 Lambda 当成神秘语法。它本质上就是“临时写一个小方法”,然后把这个小方法交给别人使用。

本页固定顺序

  1. 先学第一部分:弄懂今天最小、最重要的知识,并运行短例子。
  2. 再学第二部分:把刚学的知识组合成一个完整例子。
  3. 然后做第三部分:自己跟着敲,再完成重复训练和每日小测。
  4. 最后做第四部分:先独立完成作业,再用完整答案检查。

学习衔接

上一页学习的是“接口与抽象类”,今天继续学习“委托与 Lambda”。先使用上一页已经会的写法,再只增加今天这个新知识点;如果前置内容还不能独立敲出,先回上一页复习,不要硬跳。

今天的最低通过线

第一次学习不要求背完整页。完成下面 3 项,就可以继续:

  • 能用自己的话说明“委托与 Lambda”解决什么问题。
  • 把第一部分的短例子亲手敲完,并确认每个例子都能运行。
  • 不看完整答案完成第三部分至少前 3 个例子,再主动改一个值观察结果。

第一部分:先学原理和最小知识

这一部分只读,不敲代码。先把名词弄清楚,第三部分再完整敲代码。

1. 委托是什么

普通变量保存的是数据:

int age = 18 string name = "Tom" decimal price = 99.5

委托变量保存的是一段“可以被调用的逻辑”。

你可以这样理解:

保存什么示例说明
保存数字int count以后可以拿这个数字计算
保存文字string name以后可以拿这个文字显示
保存方法Func<int, bool> rule以后可以调用这个规则

委托最核心的一句话:

委托 = 用变量保存方法

2. 为什么要把方法保存起来

因为有些程序流程是固定的,但其中某一小段规则经常变化。

例如“筛选商品”这个流程固定:

  1. 准备一批商品。
  2. 一个一个检查。
  3. 满足条件就输出。

变化的是“满足什么条件”:

场景变化的规则
看有库存商品Stock > 0
看高价商品Price > 200
看库存充足商品Stock >= 5
看便宜商品Price <= 100

如果每个规则都写一个完整方法,重复内容会越来越多。更好的做法是:流程只写一次,规则当参数传进去。

3. 方法签名是什么

委托不是随便保存任何方法,它只保存“形状匹配”的方法。

方法的形状一般看两件事:

要看什么例子
参数这个方法需要几个输入,输入是什么类型
返回值这个方法最后返回什么类型

例如:

输入 int,返回 bool

可以对应:

Func<int, bool>

读法是:

Func<输入类型, 返回类型>

所以:

委托类型读法
Func<int, bool>输入一个 int,返回一个 bool
Func<decimal, decimal>输入一个 decimal,返回一个 decimal
Func<string, int>输入一个 string,返回一个 int
Func<int, int, int>输入两个 int,返回一个 int

注意最后一个类型永远是返回值。

4. FuncActionPredicate 的区别

这三个名字经常一起出现:

类型是否有返回值适合做什么
Func<T, TResult>有返回值判断、计算、转换
Action<T>没有返回值打印、记录日志、发送通知
Predicate<T>返回 bool专门表示判断条件

最常用的是 FuncAction

判断一个商品是否有库存:

Func<Product, bool>

把商品转换成显示文字:

Func<Product, string>

打印一行日志,不需要返回结果:

Action<string>

5. Lambda 是什么

Lambda 是一种写小方法的简短方式。

普通方法长这样:

bool IsPass(int score) { return score >= 60; }

Lambda 可以写成:

score => score >= 60

读法:

给我一个 score,返回 score >= 60 的结果

所以 => 不要读成“箭头”,而要读成:

左边是输入,右边是处理结果

6. Lambda 的三种常见形状

只有一个参数时:

score => score >= 60

多个参数时:

(price, count) => price * count

有多行逻辑时:

score => { bool passed = score >= 60; return passed ? "通过" : "未通过"; }

多行 Lambda 必须写 {},并且有返回值时必须写 return

委托和 Lambda 常用写法速查

需求写法说明
有输入有返回Func<T, TResult>转换、计算
两个输入一个返回Func<T1, T2, TResult>单价乘数量
有输入无返回Action<T>打印、回调通知
判断条件Predicate<T>返回 bool
LINQ 筛选.Where(x => 条件)Lambda 做规则
LINQ 转换.Select(x => 结果)Lambda 做映射
传方法Run(CheckPrice)不加括号
执行方法CheckPrice(100m)加括号

常用模板:

Func<decimal, int, decimal> calculateTotal = (price, count) => price * count; Action<string> log = message => Console.WriteLine(message); Predicate<decimal> isValidPrice = price => price > 0; Console.WriteLine(calculateTotal(199m, 2)); log("保存成功"); Console.WriteLine(isValidPrice(10m));

读 Lambda 时,固定按这句话:

左边是输入,右边是处理后得到的结果。

7. “传方法”和“执行方法”不是一回事

这是很容易写错的地方。

假设有一个方法:

bool IsPass(int score)

传方法给别人:

IsPass

执行方法得到结果:

IsPass(80)

区别是:

写法含义
IsPass把这个方法本身交出去
IsPass(80)现在立刻执行,得到 truefalse

Where 要的是规则,不是规则执行后的某一个结果,所以要传方法本身或 Lambda。

8. 回调是什么

回调就是:我把一段逻辑交给你,你在合适的时候再调用它。

例子:

场景谁保存回调什么时候调用
按钮点击按钮用户点击时
过滤数据Where遍历每个元素时
保存成功后通知保存方法保存完成后
读取文件每一行读取方法读到每一行时

回调不神秘,就是“稍后调用的方法”。

9. 闭包是什么

Lambda 可以使用外面的变量,这叫闭包。

例如:

decimal minPrice = 100m; product => product.Price >= minPrice

这个 Lambda 不但使用了 product,还使用了外面的 minPrice

这说明 Lambda 不是只能看自己参数,它也能记住外层作用域里的变量。

闭包很方便,但要记住:

Lambda 看到的是变量,不是变量当时的一张照片

如果外面的变量后来被改了,Lambda 使用时看到的也可能是新值。

10. 本页要形成的判断标准

以后看到一段重复流程,可以这样判断是否需要委托:

问题如果答案是“是”
流程是不是固定的?固定流程可以写成一个方法
中间某个规则是不是经常变?变化规则可以用委托传入
这个规则是不是很短?可以用 Lambda
这个规则是不是很长?写成普通方法再传进去

第二部分:把知识组合成完整例子

这一段先看效果,不要求马上理解每一行。今天学完以后,要能写出这种代码:把不同规则传给同一个处理流程。

先看效果:商品筛选器

List<Product> products = new List<Product> { new Product { Name = "键盘", Price = 199m, Stock = 8 }, new Product { Name = "鼠标", Price = 89m, Stock = 0 }, new Product { Name = "显示器", Price = 1299m, Stock = 3 }, new Product { Name = "耳机", Price = 299m, Stock = 12 } }; void PrintProducts(string title, List<Product> source, Func<Product, bool> rule) { Console.WriteLine(title); foreach (Product product in source.Where(rule)) { Console.WriteLine($"{product.Name} | 价格: {product.Price} | 库存: {product.Stock}"); } Console.WriteLine(); } PrintProducts("有库存的商品", products, product => product.Stock > 0); PrintProducts("价格超过 200 的商品", products, product => product.Price > 200m); PrintProducts("库存充足并且价格不超过 300 的商品", products, product => product.Stock >= 5 && product.Price <= 300m); class Product { public string Name { get; set; } = ""; public decimal Price { get; set; } public int Stock { get; set; } }

这个程序里最重要的不是商品,而是这一句:

Func<Product, bool> rule

它表示:PrintProducts 不固定筛选条件,而是把筛选条件交给调用者决定。

如果今天不学委托,这个程序通常会写成三个方法:

PrintProductsInStock PrintProductsExpensive PrintProductsEnoughStockAndCheap

方法越来越多,重复代码也越来越多。委托解决的就是这个问题:固定流程只写一次,可变规则从外面传进来。

第三部分:跟着敲代码

从这里开始动手。每个例子都是完整代码,可以直接放进 Program.cs 运行。

动手前先做这 3 件事

  1. 打开一个控制台项目。
  2. 每次只保留一个例子的代码,运行通过后再换下一个。
  3. 运行后改一个数字或一段文字,再运行一次,看输出是否变化。

例子 1:先不用 Lambda,只用普通方法

List<int> scores = new List<int> { 45, 60, 88, 92 }; foreach (int score in scores) { bool IsPass(int score) { return score >= 60; } if (IsPass(score)) { Console.WriteLine($"通过成绩: {score}"); } }

这个版本没有委托,只是普通方法调用。先确认你能看懂:循环每个成绩,然后用 IsPass 判断。

例子 2:把普通方法保存到委托变量里

List<int> scores = new List<int> { 45, 60, 88, 92 }; Func<int, bool> rule = IsPass; foreach (int score in scores) { if (rule(score)) { Console.WriteLine($"通过成绩: {score}"); } } bool IsPass(int score) { return score >= 60; }

这里最关键的是:

Func<int, bool> rule = IsPass;

rule 现在保存了 IsPass 这段判断逻辑。后面调用 rule(score),效果和调用 IsPass(score) 一样。

例子 3:把普通方法改成 Lambda

List<int> scores = new List<int> { 45, 60, 88, 92 }; Func<int, bool> rule = score => score >= 60; foreach (int score in scores) { if (rule(score)) { Console.WriteLine($"通过成绩: {score}"); } }

这一句:

score => score >= 60

等价于一个很短的方法:

输入 score,返回 score 是否大于等于 60

例子 4:把规则传给一个方法

List<int> scores = new List<int> { 45, 60, 88, 92 }; void PrintMatchedScores(string title, List<int> source, Func<int, bool> rule) { Console.WriteLine(title); foreach (int score in source) { if (rule(score)) { Console.WriteLine(score); } } Console.WriteLine(); } PrintMatchedScores("及格成绩", scores, score => score >= 60); PrintMatchedScores("优秀成绩", scores, score => score >= 90); PrintMatchedScores("不及格成绩", scores, score => score < 60);

这就是委托真正有用的地方:PrintMatchedScores 只写一次,但可以接收不同规则。

例子 5:Func 有返回值

Func<int, int> doubleValue = number => number * 2; Func<int, int> squareValue = number => number * number; int value = 6; Console.WriteLine($"原始数字: {value}"); Console.WriteLine($"乘以 2: {doubleValue(value)}"); Console.WriteLine($"平方: {squareValue(value)}");

Func<int, int> 的意思是:

输入 int,返回 int

例子 6:Func 可以有多个输入

Func<decimal, int, decimal> calculateTotal = (price, count) => price * count; decimal total1 = calculateTotal(19.9m, 3); decimal total2 = calculateTotal(88m, 2); Console.WriteLine($"第一笔合计: {total1}"); Console.WriteLine($"第二笔合计: {total2}");

Func<decimal, int, decimal> 的读法是:

输入 decimal 和 int,返回 decimal

最后一个类型是返回值。

例子 7:Action 没有返回值

Action<string> log = message => { string time = DateTime.Now.ToString("HH:mm:ss"); Console.WriteLine($"[{time}] {message}"); }; log("开始保存订单"); log("订单保存成功");

Action<string> 的意思是:

输入 string,不返回任何结果

适合做打印、记录日志、发送通知这类动作。

例子 8:Predicate 专门表示判断条件

Predicate<int> isEven = number => number % 2 == 0; List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 }; foreach (int number in numbers) { if (isEven(number)) { Console.WriteLine($"偶数: {number}"); } }

Predicate<int> 基本等价于:

Func<int, bool>

只不过它的名字更强调“这是一个判断条件”。

例子 9:把 Lambda 用到 LINQ 的 Where

List<string> names = new List<string> { "Tom", "Jerry", "Alice", "Jack" }; List<string> longNames = names .Where(name => name.Length >= 5) .ToList(); foreach (string name in longNames) { Console.WriteLine(name); }

Where 要的是一个判断规则:

输入一个元素,返回 true 或 false

返回 true 的留下,返回 false 的过滤掉。

例子 10:把 Lambda 用到 LINQ 的 Select

List<decimal> prices = new List<decimal> { 10m, 20m, 35m }; List<string> labels = prices .Select(price => $"价格: {price} 元") .ToList(); foreach (string label in labels) { Console.WriteLine(label); }

Select 要的是一个转换规则:

输入一个元素,返回转换后的新结果

例子 11:多行 Lambda

List<int> scores = new List<int> { 45, 60, 88, 96 }; List<string> reports = scores .Select(score => { if (score >= 90) { return $"优秀: {score}"; } if (score >= 60) { return $"通过: {score}"; } return $"未通过: {score}"; }) .ToList(); foreach (string report in reports) { Console.WriteLine(report); }

多行 Lambda 适合逻辑稍微多一点的情况。如果继续变复杂,就应该改成普通方法。

例子 12:Lambda 使用外部变量

decimal minPrice = 100m; List<decimal> prices = new List<decimal> { 59m, 120m, 300m, 80m }; List<decimal> matched = prices .Where(price => price >= minPrice) .ToList(); foreach (decimal price in matched) { Console.WriteLine($"满足最低价格: {price}"); }

这里的 Lambda 用到了外面的 minPrice。这就是闭包的常见用法。

例子 13:闭包看到的是变量的新值

int limit = 10; Func<int, bool> greaterThanLimit = number => number > limit; Console.WriteLine(greaterThanLimit(12)); limit = 20; Console.WriteLine(greaterThanLimit(12));

运行结果会先输出 True,再输出 False

原因是 Lambda 记住的是变量 limit,不是 limit 当时的值。

例子 14:传方法,不是执行方法

List<int> scores = new List<int> { 45, 60, 88, 92 }; List<int> passed = scores .Where(IsPass) .ToList(); foreach (int score in passed) { Console.WriteLine(score); } bool IsPass(int score) { return score >= 60; }

这里传的是:

IsPass

不是:

IsPass(80)

Where 会自己把每个成绩传给 IsPass

例子 15:自己写一个 Where 的简化版

List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 }; List<int> Filter(List<int> source, Func<int, bool> rule) { List<int> matched = new List<int>(); foreach (int item in source) { if (rule(item)) { matched.Add(item); } } return matched; } List<int> result = Filter(numbers, number => number % 2 == 0); foreach (int number in result) { Console.WriteLine(number); }

这个例子能帮你理解 Where 的思想:

  1. 它收到一批数据。
  2. 它收到一个判断规则。
  3. 它逐个检查。
  4. 符合规则的留下。

例子 16:自己写一个 Select 的简化版

List<int> numbers = new List<int> { 1, 2, 3 }; List<string> Map(List<int> source, Func<int, string> converter) { List<string> result = new List<string>(); foreach (int item in source) { string text = converter(item); result.Add(text); } return result; } List<string> result = Map(numbers, number => $"数字是 {number}"); foreach (string text in result) { Console.WriteLine(text); }

converter 的意思是转换器。它负责把 int 转成 string

例子 17:回调动作

void SaveOrder(string orderNo, Action<string> afterSaved) { Console.WriteLine($"正在保存订单: {orderNo}"); Console.WriteLine("保存完成"); afterSaved($"订单 {orderNo} 已保存"); } SaveOrder("A001", message => Console.WriteLine($"控制台日志: {message}"));

afterSaved 就是回调。保存方法不关心外面要怎么通知,只负责在保存完成后调用它。

例子 18:回调可以换成不同动作

void SaveOrder(string orderNo, Action<string> notify) { Console.WriteLine($"保存订单: {orderNo}"); notify($"订单 {orderNo} 保存成功"); Console.WriteLine(); } SaveOrder("A001", message => Console.WriteLine($"普通提示: {message}")); SaveOrder("A002", message => Console.WriteLine($"重要提醒: {message.ToUpper()}"));

同一个 SaveOrder 方法,传入不同 notify,最终表现就不同。

例子 19:商品规则完整练习

List<Product> products = new List<Product> { new Product { Name = "键盘", Price = 199m, Stock = 8 }, new Product { Name = "鼠标", Price = 89m, Stock = 0 }, new Product { Name = "显示器", Price = 1299m, Stock = 3 }, new Product { Name = "耳机", Price = 299m, Stock = 12 } }; void Print(string title, List<Product> source, Func<Product, bool> rule) { Console.WriteLine(title); foreach (Product product in source) { if (rule(product)) { Console.WriteLine($"{product.Name} | {product.Price} | {product.Stock}"); } } Console.WriteLine(); } Print("有库存", products, product => product.Stock > 0); Print("价格低于 300", products, product => product.Price < 300m); Print("有库存并且价格低于 300", products, product => product.Stock > 0 && product.Price < 300m); class Product { public string Name { get; set; } = ""; public decimal Price { get; set; } public int Stock { get; set; } }

到这里,你已经把今天的核心都用上了:委托变量、Lambda、规则传参、对象筛选。

常见错误和修法

错误为什么错修法
Lambda 写太长可读性差,难调试超过几行就提取成方法
委托参数顺序记不清调用时容易传错给参数起清楚名字,先看委托定义
捕获外部变量后被修改Lambda 使用的是变量,不是当时的值循环里先复制到局部变量
FuncAction 混淆一个有返回值,一个没有有返回值用 Func,只做动作用 Action
过度使用委托简单逻辑变复杂只有需要传递行为时再用

小白重复敲写训练

委托和 Lambda 必须从“把方法放进变量”开始练。

训练 1:Action 保存一个动作

Action sayHello = () => Console.WriteLine("Hello"); sayHello(); sayHello();

改动任务:让它接收一个名字,类型改成 Action<string>

训练 2:Func 保存一个计算

Func<int, int, int> add = (a, b) => a + b; int result = add(2, 3); Console.WriteLine(result);

第二遍写一个 multiply,返回两个数的乘积。

训练 3:Lambda 配合集合筛选

var numbers = new List<int> { 1, 2, 3, 4, 5 }; Func<int, bool> isEven = number => number % 2 == 0; foreach (int number in numbers.Where(isEven)) { Console.WriteLine(number); }

第三遍改成筛选大于 3 的数字。

每日小测

做完本页后,用这 5 题检查是否真的掌握。

1. 判断题

本页的目标不是只把代码运行起来,还要能说清楚“为什么这样写”。

答案:对。能运行只是第一步,能解释原理、常用操作和常见错误,才说明本页内容进入了可复用能力。

2. 填空题

本页主题是:委托与 Lambda。今天至少要掌握的 3 个点是:

1. 委托为什么存在,它解决什么问题 2. `Func`、`Action`、`Predicate` 分别什么时候用 3. Lambda 的完整读法,不再只会照着写 `=>`

答案:以上 3 点必须能用自己的代码跑通,不能只停留在阅读。

3. 流程题

遇到本页相关功能时,先按什么顺序处理?

答案:先看完整例子,确认最终效果;再读原理和名词;然后跟着第三部分从空项目敲代码;最后对照作业答案检查。

4. 找错误题

如果本页代码运行失败,第一步应该做什么?

答案:先看终端或 IDE 里的第一条错误,找到文件名和行号;不要同时改很多地方。再回到本页的“常见错误和修法”表格,对照错误类型逐项排查。

5. 改需求题

在本页完整例子跑通后,至少改一个小需求。

可选改法:

  • 改一个字段名称。
  • 多加一个校验条件。
  • 多输出一行结果。
  • 把固定数据改成用户输入。
  • 把一次处理改成多条数据处理。

答案标准:修改后能重新运行,并能说明这次修改影响了哪一段逻辑。重点检查:委托为什么存在,它解决什么问题。

上位机专项练习

委托和 Lambda 可以传递报警规则、数据换算和日志动作。

下面 3 个例子都要亲手敲。先运行原代码,再完成每个例子后面的改动任务。

专项例子 1:Func 定义报警规则

Func<double, bool> isAlarm = value => value >= 80; Console.WriteLine(isAlarm(82.5)); Console.WriteLine(isAlarm(70.0));

运行结果或界面效果:

True False

改动任务: 改成低温报警规则 value <= 10

专项例子 2:Action 传入日志动作

void ReadDevice(Action<string> log) { log("开始读取"); log("读取完成"); } ReadDevice(message => Console.WriteLine($"[设备] {message}"));

运行结果或界面效果:

[设备] 开始读取 [设备] 读取完成

改动任务: 给日志增加当前时间。

专项例子 3:Lambda 换算工程值

Func<int, double> convert = raw => raw / 4095.0 * 100.0; Console.WriteLine($"{convert(2048):F2}%"); Console.WriteLine($"{convert(4095):F2}%");

运行结果或界面效果:

50.01% 100.00%

改动任务: 把量程改成 0-200。

第四部分:作业完整答案

这一部分给出当天作业的完整答案。建议先把题目读完,再直接照着完整代码敲一遍。

作业 1:成绩处理器

要求:

  1. 准备一组成绩。
  2. 写一个方法,接收标题、成绩列表、判断规则。
  3. 分别输出及格成绩、优秀成绩、不及格成绩。

完整答案

List<int> scores = new List<int> { 45, 59, 60, 76, 88, 95 }; void PrintScores(string title, List<int> source, Func<int, bool> rule) { Console.WriteLine(title); foreach (int score in source) { if (rule(score)) { Console.WriteLine(score); } } Console.WriteLine(); } PrintScores("及格成绩", scores, score => score >= 60); PrintScores("优秀成绩", scores, score => score >= 90); PrintScores("不及格成绩", scores, score => score < 60);

这份答案要看懂三点:

  1. PrintScores 是固定流程。
  2. score => score >= 60 是变化规则。
  3. rule(score) 是执行传进来的规则。

作业 2:价格转换器

要求:

  1. 准备一组价格。
  2. Func<decimal, string> 把价格转成显示文字。
  3. 输出每个转换结果。

完整答案

List<decimal> prices = new List<decimal> { 19.9m, 88m, 1299m }; Func<decimal, string> toPriceText = price => $"价格: {price} 元"; foreach (decimal price in prices) { string text = toPriceText(price); Console.WriteLine(text); }

这份答案要看懂一点:

Func<decimal, string>

表示输入价格,返回显示文字。

作业 3:日志回调

要求:

  1. 写一个 SaveCustomer 方法。
  2. 方法内部模拟保存客户。
  3. 保存完成后调用外面传进来的 Action<string>

完整答案

void SaveCustomer(string name, Action<string> afterSaved) { Console.WriteLine($"开始保存客户: {name}"); Console.WriteLine("写入数据库完成"); afterSaved($"客户 {name} 已保存"); Console.WriteLine(); } SaveCustomer("张三", message => Console.WriteLine($"日志: {message}")); SaveCustomer("李四", message => Console.WriteLine($"提醒: {message}"));

这份答案要看懂两点:

  1. SaveCustomer 不决定怎么通知。
  2. 调用者通过 Lambda 决定保存完成后做什么。

作业 4:商品规则引擎

要求:

  1. 定义 Product 类。
  2. 准备商品列表。
  3. 写一个通用打印方法。
  4. 使用不同 Lambda 输出不同商品。

完整答案

List<Product> products = new List<Product> { new Product { Name = "键盘", Price = 199m, Stock = 8 }, new Product { Name = "鼠标", Price = 89m, Stock = 0 }, new Product { Name = "显示器", Price = 1299m, Stock = 3 }, new Product { Name = "耳机", Price = 299m, Stock = 12 }, new Product { Name = "数据线", Price = 29m, Stock = 50 } }; void PrintProducts(string title, List<Product> source, Func<Product, bool> rule) { Console.WriteLine(title); foreach (Product product in source) { if (rule(product)) { Console.WriteLine($"{product.Name} | 价格: {product.Price} | 库存: {product.Stock}"); } } Console.WriteLine(); } PrintProducts("有库存的商品", products, product => product.Stock > 0); PrintProducts("价格不超过 200 的商品", products, product => product.Price <= 200m); PrintProducts("库存不少于 10 的商品", products, product => product.Stock >= 10); PrintProducts("有库存并且价格不超过 200 的商品", products, product => product.Stock > 0 && product.Price <= 200m); class Product { public string Name { get; set; } = ""; public decimal Price { get; set; } public int Stock { get; set; } }

这份答案就是本页的核心总结:

  1. Product 是数据结构。
  2. PrintProducts 是固定流程。
  3. Func<Product, bool> 是变化规则。
  4. 每个 Lambda 都是一条具体业务规则。

作业 5:把复杂 Lambda 改成普通方法

要求:

  1. 准备成绩列表。
  2. 写一个 GetLevel 方法,把成绩转成等级文字。
  3. Select(GetLevel) 完成转换。

完整答案

List<int> scores = new List<int> { 45, 60, 75, 88, 96 }; List<string> levels = scores .Select(GetLevel) .ToList(); foreach (string level in levels) { Console.WriteLine(level); } string GetLevel(int score) { if (score >= 90) { return $"优秀: {score}"; } if (score >= 80) { return $"良好: {score}"; } if (score >= 60) { return $"通过: {score}"; } return $"未通过: {score}"; }

这里故意不用多行 Lambda,因为等级判断已经比较长。规则变长以后,普通方法更容易读,也更容易调试。

本页最后要记住

  1. 委托让方法可以像变量一样被传递。
  2. Lambda 是临时写小方法的简短语法。
  3. Func 用于有返回值的逻辑。
  4. Action 用于只执行动作、不返回结果的逻辑。
  5. Where 需要判断规则,Select 需要转换规则。
  6. 重复流程固定、规则经常变化时,就可以考虑委托。