C# 事件的几种声明方式

206 阅读2分钟

首先,事件并不是特殊的委托,它本质上是委托字段的一个包装器(类似字段和属性的关系),对外界隐藏了委托实例的大部分功能,仅暴露 添加/移除事件处理器的功能。

  • 事件的完整声明格式:
// 当委托用来约束事件时,要以“委托名+EventHandler”命名,两个参数分别是事件的发送者和事件所传递的通知信息
public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);

// 事件发送者
public class Customer
{
    // 完整格式声明一个事件 OrderEvent
    private OrderEventHandler orderEventHandler; // 先声明一个委托类型的字段,用来保存事件处理器
    public event OrderEventHandler OrderEvent // 再声明一个事件,用来访问委托类型的字段
    {
        //事件的访问器,只有add和remove两个访问器,所以在外部,只能订阅和取消订阅事件,不能调用事件
        add => orderEventHandler += value;
        remove => orderEventHandler -= value;
    }
}

// 事件传递的信息
// 微软规定事件信息类都应该继承EventArgs基类,原因在下文
public class OrderEventArgs : EventArgs
{
    public string DishName { get; set; } //菜名
    public int Count { get; set; } //份数
}

  • 语法糖:字段式声明事件(field-like)

    这种格式看起来像是声明了一个委托类型的字段,并用event关键字修饰,但实际上是声明了一个委托。编译器会自动为事件生成一个对应的委托类型的字段,然后在事件访问器中调用委托字段。

public class Customer
{
    // 格式:public event 作为约束的委托类型 事件名
    public event OrderEventHandler OrderEvent2;
}
  • 使用EventHandler声明事件

    EventHandler是微软定义的一个委托,其定义如下:

public delegate void EventHandler(object sender, EventArgs e);

可以看到它的第二个参数就是EventArgs类,如果我们在生命事件消息类时,统一按照微软的规定,继承了EventArgs类,那么就可以很方便地使用EventHandler来声明事件:

public class Customer
{
    public event EventHandler OrderEvent;  //使用EventHandler声明事件
    public void OnOrder()
    {
        OrderEventArgs e = new OrderEventArgs { DishName = "鱼香肉丝", Count = 2 };
        OrderEvent?.Invoke(this, e);
    }
}

public class Waiter
{
    // 事件处理器,参数要与EventHandler中定义的相同
    public void Action(object sender, EventArgs e)
    {
        // 使用 is 关键字类型转换
        if (sender is Customer customer && e is OrderEventArgs ea)  
        {  
            Debug.Log($"服务员:您点了{ea.Count}{ea.DishName}");  
        }  
    }
}
  • 使用泛型委托定义事件

使用微软定义的EventHandler<TEventArgs>来声明事件,这样事件消息类型就不用继承EventArgs,事件处理器中也不用对参数进行类型转换。

public event EventHandler<OrderEventArgs> OrderEvent;