C# 事件:委托字段的封装

 感谢刘铁猛老师的C#教学视频(值得反复看),以及B站UP主SunnieShine的C#教学专栏。

        事件本质是委托字段的封装。

        事件之于委托字段,相当于属性之于字段。事件本身不是字段,是字段的保护型包装器;属性不是字段,是字段的保护型包装器。

        个人理解,事件在逻辑层面的本质其实是“一个信号”,举个例子就是:“手机收到微信消息”这个事件,会触发“我拿起手机看消息”这个处理方法。

        事件的核心就是五部分:

        ①事件的拥有者

        ②事件本身

        ③事件的响应者

        ④事件的处理器(事件发生后的响应方法)

        ⑤事件的订阅关系

        上述手机收到微信的例子中,其中手机是事件的拥有者,收到微信消息是事件本身,我是事件的响应者,看消息是事件的处理器,我看消息和手机收到微信之间存在事件的订阅关系。

        在写代码来自定义事件的时候,实际也是根据上述五个要素来按步骤写即可,参考以下案例,包括了事件的完整声明格式和简约声明格式(语法糖)。C#还准备了通用事件名称EventHandler(object sender, EventArgs)。

using System;
using System.Threading;
namespace ConsoleOnlyTest
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Customer customer = new Customer();
            Waiter waiter = new Waiter();
            customer.Order += waiter.Action;  //第④步,让事件的响应者和事件之间形成订阅关系,然后利用"ctrl"+"."来生成事件处理器
            customer.Action();
            customer.PaytheBill();
        }
    }
    public delegate void OrderEventHandler(Customer customer, OrderEventArags e); //第②步的准备1,声明事件之前,先把事件需要用的委托声明出来
    //使用EventHandler后缀,是为了增加可读性,约定俗成,下方EventArags同;
    //没有自带的类可以用来传递点的菜的信息,所以新建了类Order
    public class OrderEventArags : EventArgs   //第②步的准备2,任何类如果是用来传递事件信息,需要让该类继承自EventArgs
    {   //用于传递事件信息的类,必须以EventArgs结尾
        public string DishName { get; set; }
        public string Size { get; set; }

    }
    public class Customer  //第①步,声明事件的拥有者及其事件相关的属性
    {
        public double Bill { get; set; }  //1.1
        public void PaytheBill()  //1.2
        {
            Console.WriteLine("I will pay ${0}.",Bill);
        }
        public void WalkIn()  //第⑤步的补充5.1,创建一系列触发事件处理器的方法
        {
            Console.WriteLine("Walk into the restaurant");
        }
        public void SitDown()
        {
            Console.WriteLine("Sit down.");
        }
        public void Think()  //5.1这里是关键,触发事件处理器。
        {
            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine("Let me think...");
                Thread.Sleep(1000);
            }

            //下方用this.orderEventHandler(用的是委托的封装字段)而不是直接用order事件,原因是事件只能用在+=或-=订阅操作符两边
            if (this.orderEventHandler != null)  //5.1.1首先判断委托的封装字段(也即事件)是否为空,如果为空说明根本没有订阅事件
            {
                OrderEventArags e = new OrderEventArags();  //5.1.2给下方代码补充e的信息,把点菜实例化,给点菜赋值;
                e.DishName = "Kongpao Chicken";
                e.Size = "large";
                this.orderEventHandler.Invoke(this, e); //5.1.1确认委托的封装字段不为空之后,把点菜实例化,给他参数(这时还没有e的信息)
            }
        }
        public void Action()
        {
            Console.ReadKey();
            this.WalkIn();
            this.SitDown();
            this.Think();
        }
        private OrderEventHandler orderEventHandler { get; set; }  //第②步的准备3,把声明出来的委托类型封装成委托字段,用来声明方法的时候调用

        public event OrderEventHandler Order  //真正的第②步,声明事件,注意event修饰;可以把事件看成是专门给委托类型声明的属性
        {
            add
            {
                this.orderEventHandler += value;
            }
            remove
            {
                this.orderEventHandler -= value;
            }
        }
        //public event OrderEventHandler Order; 声明事件的简化方法,可代替上述委托的封装字段和事件声明语句。
        //用了该简化声明方法的话,触发事件的方法5.1.1里就不该再引用委托的封装字段,而是直接用该事件
    }

    public class Waiter  //第③步,声明事件的响应者,此处可以先创建类但暂时没有响应的方法
    {
        public void Action(Customer customer, OrderEventArags e)  //第⑤步,创建事件处理器,该处理器必须和事件本身传递的参数类型、数量一致
        {
            Console.WriteLine("I will serve you the dish-{0}",e.DishName);
            double price = 10;
            switch(e.Size)
            {
                case "small": price *= 0.5;break;
                case "large":price *= 1.5;break;
                default:break;
            }
            customer.Bill += price;
        }
    }
}

你可能感兴趣的:(c#,.net)