事件
事件是类的一种成员,能够使类或对象具备通知能力。事件用于对象或类间的动作协调和信息传递。假设类A有某个事件,当这个事件发生时,类A会通知并传递事件参数(可选)给有订阅这个事件的类B,类B根据拿到的事件信息对事件进行响应处理。
事件模型
事件模型的5个组成部分:
1、事件的拥有者(对象或类)
2、事件成员(类成员)
3、事件的响应者(对象或类)
4、事件处理器(方法成员)
5、事件订阅(关联事件和事件处理器),当用一个事件处理器订阅事件的时候,编译器会进行类型检查。事件和事件处理器需要遵守一个“约定”,“约定”约束了事件能够将何种消息传递给事件处理器,也约束着事件处理器能处理哪些消息。“约定”实际上指的就是委托。
事件订阅解决了三个问题:
1)事件发生时,事件的拥有者会通知哪些对象。
2)使用什么样的事件处理器才能处理相应的事件。
3)事件的响应者使用何种方法来处理事件。
//例1
class Program
{
static void Main(string[] args)
{
Timer timer = new Timer();
timer.Interval = 1000;
Boy boy = new Boy();
Girl girl = new Girl();
timer.Elapsed += boy.Action; //两个事件处理器订阅timer.Elapsed事件
timer.Elapsed += girl.Action;
timer.Start();
Console.ReadLine();
}
}
class Boy
{
internal void Action(object sender, ElapsedEventArgs e)
{
Console.WriteLine("Jump!");
}
}
class Girl
{
internal void Action(object sender, ElapsedEventArgs e)
{
Console.WriteLine("Sing!");
}
}
//例子2 事件的响应者和事件的拥有者是不同的对象
class Program
{
static void Main(string[] args)
{
Form form = new Form();
Controller controller = new Controller(form);
form.ShowDialog();
}
}
class Controller
{
private Form form;
public Controller(Form form)
{
if(form != null)
{
this.form = form;
this.form.Click += this.FormClicked;
}
}
private void FormClicked(object sender, EventArgs e)
{
this.form.Text = DateTime.Now.ToString();
}
}
//例子3 事件的响应者和事件的拥有者是同一个对象
class Program
{
static void Main(string[] args)
{
MyForm form = new MyForm();
form.Click += form.Action;
form.ShowDialog();
}
}
class MyForm : Form
{
internal void Action(object sender, EventArgs e)
{
this.Text = DateTime.Now.ToString();
}
}
//例子4 事件拥有者是事件的响应者的字段成员
class Pragram
{
static void Main(string[] args)
{
MyForm form = new MyForm();
form.ShowDialog();
}
}
class MyForm : Form //事件响应者
{
private TextBox textBox;
private Button button; //事件拥有者
public MyForm()
{
this.textBox = new TextBox();
this.button = new Button();
this.Controls.Add(textBox);
this.Controls.Add(button);
this.button.Click += this.ButtonClicked; //订阅事件
this.button.Text = "Say Hello";
this.button.Top = 20;
}
private void ButtonClicked(object sender, EventArgs e) //事件处理器
{
this.textBox.Text = "Hello World!";
}
}
事件的声明
完整声明
事件是基于委托的:
1、事件需要委托类型来做约束(⭐⭐⭐);
2、记录和保存事件处理器需要借助于委托;
事件的本质是委托字段的包装器,对委托字段的访问起到限制作用,同时又对外界隐藏委托实例的大部分功能,仅对外暴露添加/移除事件处理器的功能
查看代码
using System;
using System.Threading;
namespace Code
{
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);
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("Customer: 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 thinking...");
Thread.Sleep(1000);
}
if(orderEventHandler != null)
{
OrderEventArgs e = new OrderEventArgs();
e.DishName = "Kongpao Chicken";
e.Size = "large";
this.orderEventHandler.Invoke(this, e);
}
}
public void Action()
{
Console.ReadLine();
this.WalkIn();
this.SitDown();
this.Think();
}
}
class Waiter
{
public void Action(Customer customer, OrderEventArgs e)
{
Console.WriteLine("Waiter: I will server you the dish -{0}.", e.DishName);
double price = 10;
if (e.Size == "small")
price = price * 0.5;
else if (e.Size == "large")
price = price * 1.5;
customer.Bill += price;
}
}
}
简略声明
事件的简易声明没有手动声明委托类型字段,那么事件处理器的引用存储在什么地方呢?答案是编译器会为事件准备委托类型字段存储事件处理器的引用,只不过被隐藏起来。
查看代码
using System;
using System.Threading;
namespace Code
{
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);
public class Customer
{
public event OrderEventHandler Order;
public double Bill { get; set; }
public void PayTheBill()
{
Console.WriteLine("Customer: 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 thinking...");
Thread.Sleep(1000);
}
if(this.Order != null)
{
OrderEventArgs e = new OrderEventArgs();
e.DishName = "Kongpao Chicken";
e.Size = "large";
this.Order.Invoke(this, e);
}
}
public void Action()
{
Console.ReadLine();
this.WalkIn();
this.SitDown();
this.Think();
}
}
class Waiter
{
public void Action(Customer customer, OrderEventArgs e)
{
Console.WriteLine("Waiter: I will server you the dish -{0}.", e.DishName);
double price = 10;
if (e.Size == "small")
price = price * 0.5;
else if (e.Size == "large")
price = price * 1.5;
customer.Bill += price;
}
}
}
声明事件的委托类型的命名约定
声明Xxx事件的委托,命名为XxxEventHandler。委托的参数有两个,一个是object类型,参数名为sender——表示事件的拥有者。另一个是EventArgs的派生类,类名一般为XxxEventArgs,参数名为e。触发事件的方法名一般为OnXxx,访问级别为protected,否则会被外界任意使用,造成滥用。
查看代码
using System;
using System.Threading;
namespace Code
{
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 class Customer
{
public event EventHandler Order;
public double Bill { get; set; }
public void PayTheBill()
{
Console.WriteLine("Customer: 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 thinking...");
Thread.Sleep(1000);
}
this.OnOrder("Kongpao Chicken", "large");
}
protected void OnOrder(string dishName, string size)
{
if (this.Order != null)
{
OrderEventArgs e = new OrderEventArgs();
e.DishName = dishName;
e.Size = size;
this.Order.Invoke(this, e);
}
}
public void Action()
{
Console.ReadLine();
this.WalkIn();
this.SitDown();
this.Think();
}
}
class Waiter
{
public void Action(object sender, EventArgs e)
{
Customer customer = sender as Customer;
OrderEventArgs orderInfo = e as OrderEventArgs;
Console.WriteLine("Waiter: I will server you the dish -{0}.", orderInfo.DishName);
double price = 10;
if (orderInfo.Size == "small")
price = price * 0.5;
else if (orderInfo.Size == "large")
price = price * 1.5;
customer.Bill += price;
}
}
}