Func与Action的本质区别在于返回值:Func必有返回值,Action无返回值;Predicate是Func的语义化别名,仅用于布尔判断场景。

Func 和 Action 的本质区别就一条:返回值
它们都是 .NET 内置的泛型委托类型,不是语法糖,也不是接口——是编译器直接识别、运行时原生支持的委托定义。关键差异只看有没有返回值:Func 一定有返回值,Action 一定没有。
常见错误现象:把需要返回 int 的逻辑塞进 Action,结果调用后拿不到值;或者误以为 Func 合法(它不合法,编译报错 CS0453)。
-
Func表示「接收一个int,返回一个string」 -
Action表示「接收一个string和一个bool,不返回任何东西」 -
Predicate是Func的别名,仅用于判断场景,语义更强但功能完全等价
什么时候该用 Predicate 而不是 Func
纯看意图,不看能力。两者在 IL 层和运行时完全一样,Predicate 就是 Func 的另一个名字。但当你写的是筛选、匹配、条件检查逻辑(比如传给 List 或 Array.Exists()),用 Predicate 更直白,别人一眼知道这是个“是/否”判断。
容易踩的坑:硬套 Predicate 到需要返回其他类型的地方,比如想让它返回索引或对象本身——不行,它只能返回 bool,这时候必须换 Func。
-
List—— 这里参数类型是.RemoveAll(x => x.Length > 5) Predicate,框架内部约定 -
Array.FindIndex(arr, x => x.StartsWith("A"))—— 第二个参数也是Predicate,不能改成Func - 自己定义方法时,如果参数明确是“做判断”,优先用
Predicate,提升可读性
泛型参数数量限制和实际写法差异
Action 和 Func 都有最多 16 个输入参数的重载版本(.NET Core / .NET 5+),但超过 4 个就明显难读。真正影响选择的不是上限,而是你是否需要捕获返回值、是否要复用签名。
性能上无差别,它们都是引用类型,但频繁创建闭包(比如在循环里 new Func)可能触发 GC;兼容性方面,.NET Framework 3.5 起就全支持,不用操心低版本。
-
Action有Action()(零参)到Action,没有返回值变体 -
Func必须带TResult,所以最少是Func(零参函数),最多是Func -
Predicate只有单参版本,没有多参Predicate,想多参判断就得用Func
别在事件处理中强行套用 Func/Action
事件(event)的委托类型由发布者决定,比如 EventHandler 或自定义委托。不能因为“看起来像 Action”就直接赋值 myButton.Click += (s,e) => { ... }; 然后以为这是用了 Action——它只是匿名方法语法,底层类型仍是 EventHandler。
常见错误:把 Func 当作事件回调传给第三方库,结果编译失败,提示委托类型不匹配。原因很简单:事件签名是契约,不是行为相似就能替代。
- 订阅事件必须严格匹配声明的委托类型,哪怕逻辑一模一样
- 如果库暴露的是
public event Func,那才能用OnCheck OnCheck += x => x > 0; - 绝大多数 UI 事件(
Click、KeyDown)用的是EventHandler或其泛型变体,不是Action
最常被忽略的一点:委托变量本身可以为 null,调用前不判空会抛 NullReferenceException。哪怕用了 lambda,只要没赋值给委托字段,调用时照样崩——这和普通方法调用不同,得自己兜底。











