事件声明
完整声明
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)来看,是为了约束能够使用什么样签名的方法来处理事件
-
委托类型的实例将用于存储(引用)事件处理器
事件与属性:
-
属性不是字段,很多时候属性是字段的包装器,用来保护字段不被滥用
-
时间不是委托字段,是委托字段的包装器,用来保护委托字段不被滥用
-
包装器不能被另一个包装器包装