C# 委托、事件与 Lambda:从回调到 LINQ 的一次梳理
Words: 1136Read Time: 3 minLast edited: 2026-8-4
委托到底是什么
刚开始学 C# 的时候,委托这块我一直是懵的。书上说「委托是类型安全的函数指针」,看完更懵。后来自己写 WinForm 和 Web 后台多了,才慢慢捋清楚:委托其实就是一种「方法签名类型」。
我们平时用
int、string 描述数据长什么样,委托描述的是方法长什么样——返回值是什么、参数有哪些。只要签名对得上,任何方法都能装进这个委托里,然后像变量一样传来传去。委托还支持多播:
+= 挂多个方法,调用时依次执行,这是后面事件机制的基础。Action 和 Func:不用再自己定义委托
实际写代码时很少自己用
delegate 定义新类型,因为 .NET 内置了两套泛型委托:Action:没有返回值,最多 16 个参数
Func:最后一个泛型参数是返回值类型,比如Func<int, int, int>就是两个 int 入参、int 返回
上面那个
Calculator 直接用 Func<int, int, int> 就行,一行定义都省了。我刚转 .NET 时在项目里定义了一堆委托类型,后来全被老同事换成了 Func,代码清爽不少。event 和委托的区别
事件经常和委托一起出现,我一开始以为它俩是一回事,其实差别挺关键:
- 委托是「类型」,event 是基于委托的「成员」,类似属性和字段的关系
- event 在外部只能
+=和-=,不能直接调用,也不能整体赋值
- 裸委托在外部可以随意调用、随意覆盖,谁都能把订阅列表清掉
一句话总结:event 是对委托的封装,把「触发」的权力留在类的内部,外部只能订阅和退订,不能替别人发事件。做 WinForm 的时候天天写
button.Click += xxx,用的就是这个机制。Lambda 与闭包陷阱
Lambda 表达式本质上是匿名方法,编译器会帮我们生成委托实例,语法很爽:
但 Lambda 有个经典大坑:闭包捕获的是变量本身,不是变量的值。最典型的是在循环里捕获循环变量:
因为循环结束后
i 已经变成 3,三个 Lambda 捕获的是同一个 i。解决办法是在循环里声明一个局部变量接住当前值:顺便一提,
foreach 从 C# 5 开始循环变量就是每次迭代全新的,不会有这个问题;但 for 循环到现在依然是共享的,写异步回调和 Task.Run 时尤其要小心,我在定时任务里被坑过一次,日志里所有记录都指向最后一条数据。串起来:LINQ 就是委托的流水线
理解委托之后再回头看 LINQ,突然就通透了:
Where、Select 这些方法的参数,不就是一个委托吗?Where 接收 Func<User, bool>,对每个元素执行一遍,返回 true 的留下;Select 接收 Func<User, string>,把 User 投影成 Name。所谓链式调用,就是把一个个委托串成流水线,数据从里面流过。所以我的建议是:别一上来就背 LINQ 的 API,先把委托吃透。委托是地基,事件、Lambda、LINQ 全是盖在上面的房子。
Loading...