事件
事件是类的一种成员,能够使类或对象具备通知能力。事件用于对象或类间的动作协调和信息传递。假设类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;}}
}