以下内容为学习刘铁猛老师课程的课程笔记。
事件声明
声明一个自定义事件。
事件是基于委托的:
事件需要委托来做约束,事件处理器需要和约束对应上;
事件处理器需要保存和记录,只有委托类型的实例可以做到。
委托是事件的基础,事件是委托的上层建筑。
委托是一个类,声明一个委托类型和声明一个委托类型的字段是不一样的。
事件的触发一定是事件拥有者的一些内部逻辑触发的事件。
以下代码展示了事件的完整声明:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ComputingWithCSharp
{
public class Program
{
static void Main(string[] args)
{
Customer customer = new Customer();
Waiter waiter = new Waiter();
customer.Order += waiter.Action;//事件订阅
customer.Action();
customer.PayTheBill();
}
}
public class OrderEventArgs: EventArgs //传递的事件消息
{
public string dishName { get; set; }
public string size { get; set; }
}
//声明一个委托,委托是一个类
public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);
//表明这个委托用来约束事件处理器;用来存放事件处理器
//如果在Customer类中声明,就成为了一个嵌套的类
public class Customer//拥有者
{
private OrderEventHandler orderEventHandler;
public event OrderEventHandler Order//事件
{
add
{
this.orderEventHandler += value;
}
remove
{
this.orderEventHandler -= value;
}
}
public double Bill { get; set; }
public void PayTheBill()
{
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("Let me think");
Thread.Sleep(1000);
}
if (this.orderEventHandler != null)
{
OrderEventArgs e = new OrderEventArgs();
e.dishName = "chicken";
e.size = "large";
this.orderEventHandler.Invoke(this, e);
}
}
public void Action()
{
Console.ReadLine();
this.WalkIn();
this.SitDown();
this.Think();
}
}
public class Waiter//事件响应者
{
public void Action(Customer customer, OrderEventArgs e)//事件处理器
{
Console.WriteLine("I will serve you the dish-{0}.", e.dishName);
double price = 10;
switch (e.size)
{
case "small":
price = price * 0.5;
break;
case "large":
price = price * 1.5;
break;
default:
break;
}
customer.Bill += price;
}
}
}
事件声明的简单形式:
field-like
public event OrderEventHandler Order;//事件的简单声明,很像一个字段的声明,但实质上不是
public OrderEventHandler Order;//这实际上就是声明了一个字段
为什么需要事件:程序更加有逻辑。
Customer customer = new Customer();
Waiter waiter = new Waiter();
customer.Order += waiter.Action;
OrderEventArgs e = new OrderEventArgs();
e.dishName = "Manhanquanxi";
e.size = "large";
OrderEventArgs e2 = new OrderEventArgs();
e2.dishName = "Beer";
e2.size = "large";
Customer badGuy = new Customer();
badGuy.Order += waiter.Action;
badGuy.Order.Invoke(customer, e);
badGuy.Order.Invoke(customer, e2);
customer.PayTheBill();
不使用委托的话,没有对某个方法进行限制,所以一个顾客可以调用别的顾客的方法。
事件的本质是委托字段的一个包装器,对委托字段的访问起限制作用,类似于蒙版。
事件对外界隐藏了委托实例的大部分功能,仅展示了添加/移除事件处理器的功能。
事件的命名
一般用On开头加上事件名的方式来触发事件
public void Think()
{
for (int i=0; i<5; i++)
{
Console.WriteLine("Let me think");
Thread.Sleep(1000);
}
OnOrder("chicken", "large");
}
protected void OnOrder(string dishName, string size)//需要是protected,保证不能被外界访问
{
if (this.Order != null)//判断事件封装的委托是否为空
{
OrderEventArgs e = new OrderEventArgs();
e.dishName = dishName;
e.size = size;
this.Order.Invoke(this, e);//事件只能用在订阅和取消订阅符号的左边,这里不可以用事件
}
}
命名约定:
一些误解
事件 & 委托
事件声明的时候使用了委托类型,尤其是简化的声明方式很像在声明字段;
订阅的时候用到了委托类型的实例;
完整声明中事件是不可以判断是否为空的,也不可以用来调用。
事件基于委托:
source:表明source能对外传递哪些信息;
subscriber:是一种约定,为了约束能够用什么签名来处理事件;
委托类型的实例将用于存储/引用事件处理器。
事件和属性有相似之处:
属性是字段的包装器,防止字段被滥用
事件是委托字段的包装器,防止委托字段被滥用