事件是什么?
事件是能够让对象或类具备通知能力的成员。是一种类型成员(没有产品就没有发布,没有公司就没有上市)。它是用于对象和类之间的动作协调和信息传递的
事件模型如下:
“发生->响应”
5个动作——
5个部分——
从响应者角度来看,他们除了收到事件通知,还接收到经由事件发送过来的与事件本身相关的信息,称为“事件参数”(Event Args)
被通知的人根据事件参数对事件进行响应(处理事件),(处理事件)所做的事情,称为事件处理器(Event Handler)
因此我们可以说 事件的功能就是=通知+可选的事件模型
我们可以通过一个简单的代码例子来理解5个组成部分的关系。
class Program { static void Main(string[] args) { Timer timer = new Timer();//timer是事件拥有者 timer.Interval = 1000; Boy boy = new Boy();//boy是事件响应者 timer.Elapsed += boy.Action; //Elapsed是事件 +=是订阅 timer.Start(); Console.ReadKey(); } } public class Boy { //事件处理器 internal void Action(object sender, ElapsedEventArgs e) { Console.WriteLine("hello"); } }
我们用Boy作为事件响应者 用其事件处理器Action来订阅事件拥有者Timer类对象中自带的Elapsed事件
此外对于事件我们的使用情况通常有三种:
1、事件拥有者和响应者是两个相互独立的类,响应者类的处理器订阅拥有者类的事件
namespace EventExample { 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.Clicked; } } private void Clicked(object sender, EventArgs e) { this.form.Text = DateTime.Now.ToString(); } } }
2、事件拥有者和响应者属于同一个类,这个类自己订阅自己的事件
namespace EventExample { class Program { static void Main(string[] args) { MyForm myForm = new MyForm(); myForm.Click += myForm.FormClicked; myForm.ShowDialog(); } } class MyForm : Form { internal void FormClicked(object sender, EventArgs e) { this.Text = DateTime.Now.ToString(); } } }
3、事件拥有者作为响应者的一个成员,或者是事件响应者作为拥有者的一个成员
namespace EventExample { class Program { static void Main(string[] args) { MyForm myForm = new MyForm(); myForm.ShowDialog(); } } class MyForm : Form { private TextBox TextBox; private Button button; public MyForm() { this.TextBox = new TextBox(); this.button = new Button(); this.Controls.Add(this.button); this.Controls.Add(this.TextBox); this.button.Click += this.ButtonClicked; this.button.Text = "Click"; this.button.Top = 50; } private void ButtonClicked(object sender, EventArgs e) { this.TextBox.Text = "Hello World!!!!!!!!!!!!"; } } }
上面我们用的是C#帮我们写好的事件
那我们如何来自定义一个事件呢?C#自带事件的内部实现是什么样的呢?
还记得我们之前说的,事件是基于委托的吗。这个特性会在我们声明事件时进行一个体现
委托有以下作用:
类型兼容:委托对事件做了一个约束,规定了事件和事件处理器应当匹配
存储方法的引用:委托类型的实例将这组匹配信息保存了下来
因此我们说事件需要搭配一个委托。它通常是声明在事件拥有者类外的
// 步骤1,声明delegate 如果这个委托是为了约束某个事件而声明的委托 那么通常就将其命名为“事件名+EventHandler” public delegate void MyEventHandler(object sender, System.EventArgs e);
我们可以给个更加具体的例子,比如我们声明一个点餐的委托,我们想让Customer顾客作为事件拥有者,还要传递价格、大小等事件参数。那我们就可以这样声明
public delegate void OrderEventHandler(Customer customer, EventArgs e);
之后我们就可以声明我们的事件了。(记住事件是属于事件拥有者的,它需要写在事件拥有者类的内部)
有完整声明和简略声明两种形式
完整声明:
private OrderEventHandler OrderEventHandler;//声明委托类型字段。用于存储和引用事件处理器 public event OrderEventHandler Order//声明事件Order。用OrderEventHandler来约束事件 { add//事件处理器的添加器 { this.OrderEventHandler += value; } remove//事件处理器的移除器 { this.OrderEventHandler -= value; } }
简略声明:
private event MyEventHandler myevent;
多种事件订阅格式
this.button3.Click += MyButton_Click;//第一种挂接事件方法 this.button3.Click += new EventHandler(this.MyButton_Click);//第二种 this.button3.Click += delegate (object sender, EventArgs e)//第三种 匿名方法 { this.MyTextBox.Text = "haha"; }; this.button3.Click += (sender,e) => //第四种 lambda表达式 { this.MyTextBox.Text = "hoho"; };
事件与委托的关系
事件真的是“以特殊方式声明的委托字段/实例吗”?
不是!只是声明的时候“看起来像”(对比委托字段的和事件的简化声明,field-like)
事件声明的时候使用了委托类型,简化声明造成事件看上去像一个委托的字段(实例),而event关键字则更像是一个修饰符——这就是错觉的来源之一
订阅事件的时候+=操作符后面可以是一个委托实例,这与委托实例的赋值方法语法相同,这也让事件看起来像是一个委托字段——这是错觉的又一来源
重申:事件的本质是加装在委托字段上的一个“蒙板”(mask),是个起掩蔽作用的包装器,这个用于阻挡非法操作的“蒙板”绝不是委托字段本身
为什么要使用委托类型来声明事件?
站在source的角度来看,是为了表明source能对外传递哪些消息
站在subscriber的角度来看,它是一种约定,是为了约束能够使用什么样签名的方法来处理(响应)事件
委托类型的实例将用于存储(引用)事件处理器
对比事件和属性
属性不是字段——很多时候属性是字段的包装器,这个包装器用来保护字段不被滥用
事件不是委托字段——它是委托字段的包装器,这个包装器用于保护委托字段不被滥用
包装器永远都不可能是被包装的东西
下面给出一个参考实例
namespace EventExample { class Program { static void Main(string[] args) { Customer customer = new Customer(); Waiter waiter = new Waiter(); customer.Order += waiter.Action;//挂接事件。waiter的Action订阅着customer的Order customer.Action(); customer.PayTheBill(); } } public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);//声明一个委托类型,专门用来声明事件,约束事件处理器 public class OrderEventArgs : EventArgs//声明用来传递消息的类,派生自EventArgs { public string DishName { get; set; } public string Size { get; set; } } public class Customer //事件发起者 { public event OrderEventHandler Order;//声明事件Order。用OrderEventHandler来约束事件 public double Bill { get; set; } public void Walkin() { Console.WriteLine("Walk in 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.Order != null)//等于空说明没有人订阅这个事件,会报异常 { OrderEventArgs e = new OrderEventArgs(); e.DishName = "Kongpao Chicken"; e.Size = "large"; this.Order.Invoke(this, e); } } public void Action() { Console.ReadLine(); Walkin(); Sitdown(); Think(); } public void PayTheBill() { Console.WriteLine("I Will pay ${0}.",this.Bill); } } public class Waiter //事件的响应者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; } } }