C# 委托、事件与 Lambda:从回调到 LINQ 的一次梳理

Words: 1136Read Time: 3 minLast edited: 2026-8-4

委托到底是什么

刚开始学 C# 的时候,委托这块我一直是懵的。书上说「委托是类型安全的函数指针」,看完更懵。后来自己写 WinForm 和 Web 后台多了,才慢慢捋清楚:委托其实就是一种「方法签名类型」。
我们平时用 intstring 描述数据长什么样,委托描述的是方法长什么样——返回值是什么、参数有哪些。只要签名对得上,任何方法都能装进这个委托里,然后像变量一样传来传去。
委托还支持多播:+= 挂多个方法,调用时依次执行,这是后面事件机制的基础。

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,突然就通透了:WhereSelect 这些方法的参数,不就是一个委托吗?
Where 接收 Func<User, bool>,对每个元素执行一遍,返回 true 的留下;Select 接收 Func<User, string>,把 User 投影成 Name。所谓链式调用,就是把一个个委托串成流水线,数据从里面流过。
所以我的建议是:别一上来就背 LINQ 的 API,先把委托吃透。委托是地基,事件、Lambda、LINQ 全是盖在上面的房子。
Loading...
© 2024 - 2026 ihuadz
中文