事件与委托高级用法
目录
1)事件内部其实是一条“委托链”—— 多播委托(Multicast Delegate)
button.Click += A;
button.Click += B;
button.Click += C;实际上 Click 内部是一个链:
A → B → C当事件触发时,会依次调用 A、B、C。
这是 C# 中委托的一个非常强大的机制:
委托 = 方法链
多个方法可以像珠子串起来
为什么事件比委托更安全?
如果 Click 是委托:
button.Click = A;
button.Click = B; // 覆盖掉 A但事件(event)禁止你这样做:
button.Click = A; // ❌ 编译报错这就防止了别人 覆盖你的事件列表。
2)+= 和 = 的区别
写法含义event += handler往事件链添加一个方法delegateInstance = handler覆盖整个委托链(危险)
比如:
Action a = null;
a += A;
a += B;
a = C; // A 和 B 都没了,链被替换为 C但事件不能这样:
public event Action OnChanged;
OnChanged = A; // ❌ 不允许为什么?
因为事件只允许 += 和 -=,不允许外部清空或替换
这是事件的安全性来源。
3)自定义事件的 add/remove——你想把事件存到哪里都可以
平时写的事件:
public event Action OnChanged;其实是编译器自动生成一个“私有委托字段”。
但你可以自定义事件的 add/remove:
private Action? _handler;
public event Action OnChanged
{
add
{
Console.WriteLine("有人订阅事件!");
_handler += value;
}
remove
{
Console.WriteLine("有人取消订阅事件!");
_handler -= value;
}
}这允许你:
✔ 记录谁订阅了
✔ 使用自己的事件存储方式
✔ 自定义事件触发规则
✔ 实现事件总线玩法
这属于“高手用法”。
4)为什么 EventHandler 是官方推荐写法
public event EventHandler<UserChangedEventArgs> UserChanged;这是 C# 官方推荐的事件模式。原因有三:
① 统一规范,任何人一看就知道怎么用
只要看到:
( object sender, EventArgs e )你马上知道:
- sender = 谁触发的事件
- e = 数据
所有事件统一风格。
② 有标准事件参数基类(EventArgs)
你要传什么数据都放到 EventArgs:
public class UserChangedEventArgs : EventArgs
{
public string Name { get; }
}③ 清晰表达语义:这是一个事件,而不是普通的委托
用 Action / Func 虽然能用,但不够标准。
EventHandler 明确表达:
这是一个事件,遵循“事件模式”。
5)Action / Func / Predicate 的本质
- Action
- Func
- Predicate
其实全是现成的委托类型。
名称本质Action无返回值的方法**Func
例如:
Action a; // void 方法
Func<int> f; // int 返回值
Predicate<string> p; // bool 判断方法你完全可以自己写:
public delegate void MyAction();但没必要,因为 C# 已提供 16 种 Action/Func 组合。
6)事件能不能是 async?(常见误区)
你可以让事件处理方法是 async
event EventHandler Something;
Something += async (s, e) =>
{
await Task.Delay(1000);
};这是允许的。
但事件本身不应该 async
不要这样写:
public async event Action OnChanged; // 错因为:
- 事件触发是同步调用,所以 async event 语义不清
- 多播委托中 async 方法会被忽略异常
- await 在事件链中不可控