[C#] 事件和委托

事件 Event

事件模型的5个部分

事件的拥有者 (source, 对象) --> Customer

事件成员 (event, 成员) --> Order

声明

// 声明委托类型, 与类同级
// 参数1: 事件拥有者,或者说触发事件的对象,也就是顾客
// 参数2: 事件所携带的信息, 也就是点菜的内容
public delegate void OrderEventHandler(Customer c, OrderEventArgs e);

// Customer类里, 声明事件
class Customer 
{
    // 声明一个委托类型的字段, 且不能被外界访问
    private OrderEventHanlder orderEventHandler;
    public event OrderEventHandler Order 
    {
        // 添加事件
        add
        {
            this.orderEventHandler += value;
        }

        // 移除事件
        remove
        {
            this.orderEventHandler -= value;
        }
    }
    // ...
}

注意上面声明一个委托类型时, 第二个参数需要传入一些信息, c#有专门的类EventArgs来描述事件参数

// 事件参数, 事件所携带的信息
public class OrderEventArgs : EventArgs
{
    // 声明属性
    public string DishName { get; set; }
    public string Size { get; set; }
}

也可以进行简略声明(字段式声明 field-like)⬇️

public delegate void OrderEventHandler(Customer c, OrderEventArgs e);

// Customer类里就可以只写
class Customer 
{
    // 这时候的Order是一个委托字段
    public event OrderEventHandler Order;
    // ...
}

但是这样做是有隐患的, 因为其他的Customer类对象也可以在外部触发事件

就好比顾客1点了两盘菜, 结果结账的时候发现有别人给自己又点了两盆菜

c#为了避免这种情况, 加入了EventHandler委托(通用委托)
修改上面代码

// 首先去掉委托类型的声明
// public delegate void OrderEventHandler(Customer c, OrderEventArgs e);

// Customer类里就可以只写
class Customer 
{
    // 使用通用委托类型
    public event EventHandler Order;
    // ...
}

简化声明后, 后续代码引用事件时可以直接写事件名称Order, 而不是orderEventHandler

// 检查封装的委托是否为空
if (this.Order != null)

事件的本质是委托字段的包装器(封装), 仅对外暴露添加/移除事件处理器的功能(直接使用方法名)

事件的响应者 (event subscriber, 对象) --> Waiter

事件处理器 (event handler, 成员) - 一种回调方法 --> Waiter.Action

Waiter.Action方法作为事件处理器, 会被添加到事件中去
customer.Order += waiter.Action; // 为事件添加处理器

在我们使用通用的委托类型EventHandler之前, 这个方法接受两个参数

  • 参数1: 事件拥有者Customer
  • 参数2: 事件的参数类型OrderEventArgs

而当我们使用EventHandler之后, 即Waiter.Action方法将作为一个通用的委托类型(大概这个意思), 这时候参数应当符合类的定义
public void Action(object sender, EventArgs e)

  • 参数1: 发送/触发事件的任意对象
  • 参数2: 通用的事件的参数类型EventArgs

事件订阅 - 把事件处理器与事件关联在一起

全部代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace event_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            Customer customer = new Customer();
            Waiter waiter = new Waiter();
            customer.Order += waiter.Action;  // 为事件添加处理器
            customer.Action();  // 触发事件, 同时也运行了事件处理器
            customer.PayBill(); // 
        }
    }

    // 事件类型, 事件参数
    public class OrderEventArgs : EventArgs
    {
        // 声明属性
        public string DishName { get; set; }
        public string Size { get; set; }
    }

    public class Customer
    {
        // 简化事件声明, Order就是事件的名字, 这是事件不是字段(注意), 不能用Invoke()了
        public event EventHandler Order;

        public double Bill { get; set; }
        public void PayBill()
        {
            Console.WriteLine("I will pay ${0}", this.Bill);
        }

        public void WalkIn()
        {
            Console.WriteLine("Walk into the restaurant.");
        }

        public void SitDown()
        {
            Console.WriteLine("Sit down.");
        }

        public void Think()
        {
            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine("I'm thinking...");
                Thread.Sleep(1000);
            }

            // 触发Order事件, 传入参数
            this.OnOrder("Orange Chicken", "large");
        }

        // 事件触发器
        protected void OnOrder(string dishName, string size)
        {
            // 检查封装的委托是否为空
            if (this.Order != null)
            {
                OrderEventArgs e = new OrderEventArgs();
                e.DishName = dishName;
                e.Size = size;
                this.Order.Invoke(this, e);
            }
        }

        public void Action()
        {
            Console.ReadLine();
            this.WalkIn();
            this.SitDown();
            this.Think();
        }
    }

    public class Waiter
    {
        // 事件处理器, 通用版
        public void Action(object sender, EventArgs e)
        {
            // 将通用版转为本项目中定义好的类对象
            Customer c = sender as Customer;
            OrderEventArgs o = e as OrderEventArgs;
            Console.WriteLine("Serving the dish - {0}", o.DishName);
            double price = 35;
            switch (o.Size)
            {
                case "small":
                    price = price * 0.5;
                    break;
                case "large":
                    price = price * 1.5;
                    break;
                default:
                    break;
            }
            c.Bill += price;
        }
    }
}
posted @ 2024-03-07 16:23  Akira300000  阅读(2)  评论(0编辑  收藏  举报