事件声明
完整声明
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();//这里也能直接写MyOrdercustomer.PayBill();}}//class前需加上public,否则访问级别低于OrderEventHandler会报错public class OrderEventArgs : EventArgs//继承于基类:EventArgs{ //传递事件信息,名字要以事件名+EventArgspublic 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)来看,是为了约束能够使用什么样签名的方法来处理事件
- 委托类型的实例将用于存储(引用)事件处理器
事件与属性:
- 属性不是字段,很多时候属性是字段的包装器,用来保护字段不被滥用
- 时间不是委托字段,是委托字段的包装器,用来保护委托字段不被滥用
- 包装器不能被另一个包装器包装