委托是类型安全的函数指针,描述方法签名;接口是契约,定义对象能做什么。二者不可替代:委托适用于回调、事件、策略注入等场景;接口适用于需类型识别、状态管理、职责分离或领域建模的情形。

委托和接口在C#里根本不是同一类东西
委托(delegate)是类型安全的函数指针,描述“**方法签名**”;接口(interface)是契约,定义“**对象能做什么**”。它们不构成替代关系,更不是“选一个用”的问题。强行对比容易误入歧途——比如试图用委托代替整个 IEnumerable<t></t> 或 IDisposable,结果代码会迅速失控。
该用委托的典型场景:回调、事件、策略注入
当你需要把“一段可执行逻辑”作为参数传递、延迟调用、或动态替换时,委托就是最轻量且自然的选择。它比定义单方法接口 + 实现类简洁得多,也比泛型委托(如 Func<t></t>、Action<t></t>)以外的手写委托更易维护。
- 异步完成后的处理:
Task.Run(...).ContinueWith(task => { ... })里的task => { ... }就是Action<task></task> - 集合操作中的谓词:
list.Find(x => x.Status == Active)传入的是Predicate<t></t>(本质是Func<t bool></t>) - UI事件绑定:
button.Click += (s, e) => MessageBox.Show("clicked");—— 这里+=的右边必须是匹配RoutedEventHandler或EventHandler委托类型的实例 - 临时策略切换(无需生命周期管理):
var processor = new DataProcessor(validator: x => x.Length > 0);,比起新建IValidator接口和一堆实现类,这里用Func<string bool></string>更直接
什么时候不该硬套委托?
当行为有状态、需复用、要被多个地方识别为同一种能力、或涉及资源管理时,接口更合适。委托无法表达“我是某种角色”,也不能被 is / as 检查,也不支持显式实现或默认方法(C#8+ 接口可以有默认实现)。
- 你需要
if (obj is ILogger)判断能力?用ILogger接口,别用Action<string></string> - 一个类既要记录日志,又要发送告警,还要持久化——这些职责分散且可能独立演化,适合拆成多个接口(
ILogger、IAlertService、IDataSink),而不是塞进一堆委托字段 - 委托实例本身不持有上下文语义,
Func<int> getNextId</int>看不出它来自数据库序列、雪花算法还是内存计数器;而IIdGenerator接口能明确传达意图并支持 DI 容器统一管理生命周期
委托 vs 单方法接口:性能和可读性的权衡点
从运行时看,调用委托和调用接口方法性能差异极小(JIT 优化后几乎一致)。真正影响决策的是可维护性:
- 如果这个“单一行为”未来大概率保持稳定(如过滤、映射、比较),用
Func/Action是标准做法 - 如果它背后代表一种领域概念(如
IAuthenticator、IPaymentGateway),哪怕当前只有一两个方法,也该用接口——这决定了别人怎么理解你的 API - 注意:.NET 5+ 引入了
ref struct委托(如SpanAction类型),但普通业务代码极少需要手动定义委托类型;优先复用Func/Action及其变体
最容易被忽略的一点:委托变量本身是引用类型,但捕获闭包(尤其是对局部变量或 this)可能导致意外的生命周期延长——这点比接口更容易引发内存泄漏。用的时候得心里有数。









