C#学习 - 事件 续

事件声明

完整声明

using System;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Customer customer = new Customer();
            Waiter waiter = new Waiter();
            customer.Order += waiter.Action;
            customer.Action();//这里也能直接写MyOrder
            customer.PayBill();
        }
    }
    //class前需加上public,否则访问级别低于OrderEventHandler会报错
    public class OrderEventArgs : EventArgs//继承于基类:EventArgs
    {   //传递事件信息,名字要以事件名+EventArgs
        public string DishName { get; set; }//菜名
        public string Size {  get; set; }//份量
    }
    public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);//第一个参数:点菜人;第二个参数:点了什么/点了多少
    //委托是为了声明某个事件而准备的,需要用EventHandler作为后缀
    public class Customer//事件拥有者:顾客(类)
    {   //class前需加上public,否则访问级别低于OrderEventHandler会报错
        private OrderEventHandler orderEventHandler;//委托字段,用来引用事件处理器
        public event OrderEventHandler Order//声明事件
        {
            add//事件处理器的添加器
            {
                this.orderEventHandler += value;//value == 传进来的EventHandler
            }
            remove//事件处理器的移除器
            {
                this.orderEventHandler -= value;
            }
        }
        public double Bill { get; set; }//账单(属性)
        public void PayBill()
        {
            Console.WriteLine("Total: ${0}", this.Bill);
        }
        public void MyOrder()//顾客点菜,触发事件
        {
            if(this.orderEventHandler != null)
            {
                OrderEventArgs e = new OrderEventArgs();
                Console.WriteLine("You can order dish.");
                e.DishName = Console.ReadLine();//选择菜名
                Console.WriteLine("Enter the portion size of the dish.");
                e.Size = Console.ReadLine();//选择份量
                this.orderEventHandler.Invoke(this, e);
            }
        }
        public void Action()//触发MyOrder
        {
            Console.ReadLine();
            this.MyOrder();
        }
    }
    public class Waiter//事件的响应者:服务员(类)
    {
        public void Action(Customer customer, OrderEventArgs e)
        {
            Console.WriteLine("Serving your {0}",e.DishName);
            double price = 5;//菜的价格
            switch(e.Size)
            {
                case "1":
                    price = price * 1;
                    break;
                case "2":
                    price = price * 2; 
                    break;
                case "3":
                    price = price * 4;
                    break;
                default:
                    break;
            }
            customer.Bill += price;
        }
    }
}

简略声明

字段式声明(field - like)

private OrderEventHandler orderEventHandler;
public event OrderEventHandler Order
{
    add
    {
        this.orderEventHandler += value;
    }
    remove
    {
        this.orderEventHandler -= value;
    }
}
//将上段代码删除,修改为
public event OrderEventHandler Order;
//同时,将下段代码中的orderEventHandler修改为事件名Order
public void MyOrder()
{
    if(this.orderEventHandler/*此处*/ != null)
    {
        OrderEventArgs e = new OrderEventArgs();
        Console.WriteLine("You can order dish.");
        e.DishName = Console.ReadLine();
        Console.WriteLine("Enter the portion size of the dish.");
        e.Size = Console.ReadLine();
        this.orderEventHandler/*此处*/.Invoke(this, e);
    }
}

简略声明中的语法与完整声明中有不一致处。在完整声明中,不能用Order(事件名)替代orderEventHandler(事件处理器),事件名只能处于 += 或 -=操作符左边;而简略声明中没有声明委托字段orderEventHandler,只能用Order替代(在简略声明中,编译器暗地里声明了一个名为Order的字段)

事件的必要性

简略声明事件与直接使用委托类似(只在声明时多了一个event),但事件能让程序逻辑以及对象之间的关系更加有好,让程序更加安全

//简略声明中删去event
static void Main(string[] args)
{
    Customer customer = new Customer();
    Waiter waiter = new Waiter();
    customer.Order += waiter.Action;
    customer.Action();

    OrderEventArgs e1 = new OrderEventArgs();
    e1.DishName = "Fried rice with egg";
    e1.Size = "1";
    OrderEventArgs e2 = new OrderEventArgs();
    e2.DishName = "Coke";
    e2.Size = "1";

    Customer c = new Customer();
    c.Order += waiter.Action;
    c.Order.Invoke(customer, e1);//事件不能用“.”操作符,而委托可以
    c.Order.Invoke(customer, e2);
    
    customer.PayBill();//customer被迫付了一份蛋炒饭和一份可乐的钱
}

事件本质

事件本质是委托字段的一个包装器

  • 包装器对委托字段的访问起限制作用
  • 封装(encapsulation)的一个重要功能就是隐藏
  • 事件对外界隐藏了委托实例的大部分功能,只暴露了添加&移除事件处理器的功能

事件类委托命名规则

声明事件A的委托,命名为AEventHandler(事件名 + EventHandler)。有个通用委托:public delegate void EventHandler(object sender, EventArgs e);
AEventHandler委托参数一般有两个

  • object类型:参数名为sender,事件拥有者
  • EventArgs类的派生类:类名一般为AEventArgs,参数名为e,事件参数
  • 委托的参数列表可以看作是事件发生后发送给事件响应者的“事件消息”
    触发A事件的方法一般命名为OnA,访问级别为protected而非public
    之前的代码中没有使用OnOrder,若要遵守规则,则需修改:
public void MyOrder()
{
		if(this.orderEventHandler != null)
	{
		OrderEventArgs e = new OrderEventArgs();
		Console.WriteLine("You can order dish.");
		e.DishName = Console.ReadLine();//选择菜名
		Console.WriteLine("Enter the portion size of the dish.");
		e.Size = Console.ReadLine();//选择份量
		this.orderEventHandler.Invoke(this, e);
	}
}
//将事件声明中的上段代码修改为下面的
public void MyOrder()
{
	Console.WriteLine("You can order dish.");
	string dish = Console.ReadLine();
	Console.WriteLine("Enter the portion size of the dish.");
	string size = Console.ReadLine();
    this.OnOrder(dish, size);
}
protected void OnOrder(string dish, string size)
{
    if(this.orderEventHandler != null)
    {
        OrderEventArgs e = new OrderEventArgs();
        e.DishName = dish;
        e.Size = size;
        this.orderEventHandler.Invoke(this, e);//事件拥有者触发事件,传this(this就是事件拥有者)
    }
}
//简略声明就把orderEventHandler改为Order

事件命名

带有时态的动词或动词短语
命名时要使用对应的时态

事件与委托的关系

事件不是以特殊方式声明的委托字段或实例:

  • 事件进行简略声明时很像委托字段
  • 订阅事件时 += 操作符后面可以是一个委托实例,与委托实例的赋值方法语法相同,让事件看起来像个字段customer.Order += new EventHandler(waiter.Action);

委托类型来声明字段:

  • 在事件拥有者(source)来看,是为了表明能向外传递哪些信息
  • 在事件响应者(subscriber)来看,是为了约束能够使用什么样签名的方法来处理事件
  • 委托类型的实例将用于存储(引用)事件处理器

事件与属性:

  • 属性不是字段,很多时候属性是字段的包装器,用来保护字段不被滥用
  • 时间不是委托字段,是委托字段的包装器,用来保护委托字段不被滥用
  • 包装器不能被另一个包装器包装
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值