目录

事件与委托高级用法

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有返回值的方法Predicate**返回 bool 的方法(判断用)

例如:

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 在事件链中不可控