2026-08-01
CSharp编程
0

目录

一、拆解 Func 的含义
二、在代码中的实际作用
三、Func 与普通委托的关系
四、Func 的变体
五、生活类比总结
六、一句话总结
一、执行过程步骤
二、是否都在一个线程执行?
执行线程追踪
三、生活类比
四、什么情况下会跨线程?
五、总结
一、对比:普通方法 vs Func 委托
普通方法调用(写死)
Func 委托(可插拔)
二、生活类比
普通方法调用 = 固定的专卖店
Func 委托 = 自助饮料机
三、委托解决的三个核心问题
具体场景举例
四、委托 vs 接口:都能解耦,委托更轻量
五、总结:委托的本质价值

问:int Calculate(int a, int b, Func<int, int, int> operation) { return operation(a, b); } 以上代码里的func是什么作用?

Func<int, int, int> 在这里的作用是:定义了一个能接收两个 int 参数、并返回一个 int 结果的委托类型**,作为方法的“算法参数”。

简单理解:它就是一个可以让你“把计算方法塞进方法里”的参数


一、拆解 Func 的含义

Func<int, int, int> 中:

  • 最后一个 int:代表方法的返回值类型
  • 前面的 int, int:代表方法的两个参数类型

所以这个委托可以指向任何符合以下签名的方法:

csharp
int 任意方法名(int x, int y)

生活类比
这就像在淘宝买一个“智能炒菜机”,它有一个“烹饪模式”插槽(Func),这个插槽要求:

  • 插入的芯片必须有两个输入(食材1、食材2)
  • 芯片必须输出一个结果(成品菜)

你不能把“只听音乐”的芯片插进去(参数不对),也不能把输出是“字符串”的芯片插进去(返回类型不对)。


二、在代码中的实际作用

Calculate 方法本身只负责执行,但不关心具体怎么计算。计算逻辑通过 operation 参数从外部传入。

使用示例

csharp
// 传入加法 int sum = Calculate(5, 3, (a, b) => a + b); // 返回 8 // 传入减法 int diff = Calculate(5, 3, (a, b) => a - b); // 返回 2 // 传入乘法 int product = Calculate(5, 3, (a, b) => a * b); // 返回 15

如果没有 Func,你只能写三个方法:

csharp
int Add(int a, int b) => a + b; int Subtract(int a, int b) => a - b; int Multiply(int a, int b) => a * b;

调用时写死方法名,无法动态切换算法。


三、Func 与普通委托的关系

Func 是 .NET 内置的泛型委托,省去了你手动声明委托的麻烦。

不用 Func 的原始写法

csharp
// 需要先声明一个委托类型 public delegate int MyMathOperation(int a, int b); int Calculate(int a, int b, MyMathOperation operation) { return operation(a, b); }

用 Func 的简化写法

csharp
int Calculate(int a, int b, Func<int, int, int> operation) { return operation(a, b); }

所以 Func 的作用就是:让你不用自己写 delegate 声明,直接使用现成的泛型委托。


四、Func 的变体

Func 类型参数个数返回值
Func<int>0 个参数int
Func<int, string>1 个 int 参数string
Func<int, int, bool>2 个 int 参数bool
Func<string, double, int, float>3 个参数(string, double, int)float

最多支持 16 个参数。

另一个常用委托 Action:与 Func 类似,但没有返回值

csharp
Action<string> log = (msg) => Console.WriteLine(msg); // 无返回值

五、生活类比总结

概念生活类比
Func<int, int, int>一个“算法插槽”的规格说明书
(a, b) => a + b符合规格的“加法芯片”
Calculate 方法一个“计算器”,里面有个插槽可以插芯片
调用 Calculate 传入 (a,b)=>a+b把加法芯片插进计算器,机器执行加法

六、一句话总结

Func<int, int, int> 是一个委托类型,它定义了“能接收两个 int 并返回一个 int”的方法签名,让你可以把具体计算方法当作参数传给 Calculate 方法,从而实现算法的动态切换。

问:以上func功能,执行过程,是怎样的步骤?是否都在一个线程执行?

一、执行过程步骤

以这段代码为例:

csharp
int result = Calculate(5, 3, (a, b) => a + b);

步骤拆解

步骤做什么代码层面
1准备参数计算 53 的值
2创建委托实例(a, b) => a + b 编译成一个匿名方法,并创建 Func<int,int,int> 委托指向它
3调用 Calculate 方法53、以及刚才创建的委托实例传入
4进入 Calculate 方法体执行 return operation(a, b);
5通过委托调用目标方法调用 (a, b) => a + b,传入 a=5, b=3
6执行加法逻辑计算 5 + 3 = 8
7返回值层层返回加法返回 8Calculate 返回 8 → 赋值给 result

关键点:步骤 5 中的 operation(a, b) 看起来像普通方法调用,但实际上是通过委托实例间接调用目标方法。


二、是否都在一个线程执行?

是的,完全在同一个线程中执行,不会自动创建新线程。

执行线程追踪

text
主线程(假设是 UI 线程或 Main 线程): ├─ 步骤1-2:准备参数和委托(主线程) ├─ 步骤3:调用 Calculate(主线程) ├─ 步骤4:进入 Calculate 方法体(主线程) ├─ 步骤5:通过委托调用 Lambda(主线程) ├─ 步骤6:执行加法(主线程) └─ 步骤7:返回结果(主线程)

整个过程是同步、阻塞的——Calculate 方法不会立即返回,而是要等 operation(a, b) 执行完才会返回。


三、生活类比

同一线程执行就像你亲自做三明治

  1. 你拿出面包(准备参数)

  2. 你拿出火腿(创建委托)

  3. 你把火腿夹进面包(调用方法)

  4. 你咬一口(执行委托内的逻辑)

  5. 你咽下去(返回值)

全部是你自己(同一个线程)完成的,没有叫别人帮忙。

如果开启新线程(比如用 Task.Run),就像你叫外卖

  • 你下单后可以继续做其他事(主线程不阻塞)

  • 外卖员在另一个线程里做三明治

  • 做好后通知你

但上面 Func 的例子没有这种操作,所以不涉及多线程。


四、什么情况下会跨线程?

Func 本身不会跨线程,但可以把 Func 传给会开新线程的方法:

csharp
// 这会开新线程执行委托 Task.Run(() => Calculate(5, 3, (a,b) => a + b));

或者:

csharp
// 委托内部自己开线程 Func<int,int,int> func = (a,b) => { return Task.Run(() => a + b).Result; // 危险写法,仅为示例 };

但在常规用法中(如 LINQ、List.Sort、上面的 Calculate),委托和调用它的方法永远在同一个线程


五、总结

问题答案
执行过程是怎样的?普通方法调用流程:准备参数 → 调用 Calculate → 内部通过委托调用 Lambda → 返回
是否在一个线程执行?,完全在同一个线程,同步执行
Func 会自己开线程吗?不会,它只是委托,不涉及任何线程操作
委托调用比普通调用慢吗?有极微小的性能开销(委托调用需要一次间接寻址),但通常可以忽略

一句话记住Func 委托就是一个可在代码中传递的方法引用,它的执行过程和普通方法调用基本一样,同步、不跨线程

问:Func 委托执行过程和普通方法调用基本一样,那Func 委托的存在意义是什么?

这是一个很深刻的问题。既然执行过程几乎一样,那 Func 委托存在的意义是什么?

核心答案是:它把“调用哪个方法”这个决定权,从编译时推迟到了运行时。

换句话说,普通方法调用是“写死”的,而委托是“可插拔”的。


一、对比:普通方法 vs Func 委托

普通方法调用(写死)

csharp
int Add(int a, int b) => a + b; int result = Add(5, 3); // 编译时就确定调用的是 Add

特点:你在写代码的那一刻,就已经决定了要调用 Add。如果想改成减法,必须修改代码、重新编译。

Func 委托(可插拔)

csharp
int Calculate(int a, int b, Func<int, int, int> operation) { return operation(a, b); } // 运行时决定调用什么 int result1 = Calculate(5, 3, (a,b) => a + b); // 加法 int result2 = Calculate(5, 3, (a,b) => a - b); // 减法 int result3 = Calculate(5, 3, (a,b) => a * b); // 乘法

特点Calculate 方法本身完全不关心具体是什么算法,调用方可以在运行时自由选择。


二、生活类比

普通方法调用 = 固定的专卖店

你去一家只卖咖啡的店。你每次去,不用说话,店员直接给你一杯美式。

  • 优点:简单、快

  • 缺点:想喝果汁?去另一家店(改代码)

Func 委托 = 自助饮料机

一台机器有一个“制作饮料”的插槽(Func),你可以插入不同的配方芯片:

  • 插入咖啡配方 → 出咖啡
  • 插入果汁配方 → 出果汁
  • 插入奶茶配方 → 出奶茶

机器的代码(Calculate)不用改,换芯片(传不同的委托)就能改变行为。


三、委托解决的三个核心问题

问题普通方法Func 委托
算法可替换吗否,写死了是,调用时决定
方法能当参数传吗不能
能延迟执行吗不能(立即执行)能(存起来以后用)

具体场景举例

场景1:LINQ 的 Where 筛选

csharp
// 筛选条件不是写死的,调用者决定 var result = list.Where(x => x.Age > 18); // 条件1 var result = list.Where(x => x.Name.StartsWith("张")); // 条件2

如果 Where 不用委托,你就要写 WhereAgeGreaterThan18()WhereNameStartsWithZhang() 等无数个方法。

场景2:排序规则

csharp
// 今天按年龄排,明天按姓名排 list.Sort((a,b) => a.Age.CompareTo(b.Age)); list.Sort((a,b) => a.Name.CompareTo(b.Name));

场景3:回调

csharp
// 下载完成后做什么,由调用方决定 DownloadFile(url, () => Console.WriteLine("下载完成")); DownloadFile(url, () => SendEmail());

四、委托 vs 接口:都能解耦,委托更轻量

有人可能会问:“那用接口也能实现同样的效果,为什么用委托?”

对比接口委托
需要定义新类型吗需要(定义一个 ICalculator 接口)不需要(直接用 Func
代码量较多较少
灵活性一个接口可以有多个方法委托只关注单个方法签名
适用场景多个相关操作的组合单个操作/算法的注入

简单原则:只需要传一个方法 → 用委托;需要传一组相关方法 → 用接口。


五、总结:委托的本质价值

层面说明
语法上把方法变成一种可以传递、赋值、存储的对象
设计上实现策略模式,把算法和调用方解耦
运行时让调用方决定具体执行什么逻辑,而不是写死在方法内部

一句话记住

普通方法调用是“我知道我要做什么,我直接做”,
委托是“我不知道你要做什么,你来告诉我,我帮你做”。

这就是 Func 委托存在的意义——不是执行方式不同,而是代码的组织方式和灵活度完全不同

本文作者:ZeroEngineer

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!