C#委托、事件剖析(下)
本节对事件进行总结。
二、事件:
1、概念:Event:A member that enables an object or class to provide notifications;官方的解释是这样,就是说在C#中,事件是使
对象或者类具备通知能力的成员。比如说手机收到短信提醒我去开会,那么手机就充当了一个具备通知能力的成员。说白了,事件
的作用就是对象和类之间的信息传递的桥梁。
2、原理:源于发生-响应模型:
事件源(event source) + 事件本身(event) => 事件的订阅者(event subscriber) + 事件处理器(event handler)
(另外还有事件的订阅者和事件源之间的订阅关系subscribe relationship)
还是以手机收到短信提醒我去开会为例,事件源:手机吗,事件:收到短信,事件的订阅者:我,事件处理器:去开会,订阅关系:我订阅手机
3、事件的声明:分为详细声明和简略声明:
(1)详细声明:
public delegate void MyDelegateEventHandler(); public class Event { private MyDelegateEventHandler myDelegateEventHandler; public event MyDelegateEventHandler MyDelegate { add { this.myDelegateEventHandler += value; } remove { this.myDelegateEventHandler -= value; } } }
(2)简略说明:
public delegate void MyDelegateEventHandler(); public class Event { public event MyDelegateEventHandler myDelegate; }
可以看到,在完整声明中首先添加了一个委托类型的字段,然后暴漏了添加和移除事件处理器的功能,但是我们经常用的是简略声明,因为代码更加简洁,
可以看出事件对外界隐藏了大部分功能,它的本质就是对其中委托字段的一个封装(encapsulation),防止外界偷用滥用委托字段。
那么问题来了:第一个问题:有了委托为什么还会有事件呢,事件内部不就是委托吗,原因是为了防止public型的委托字段在外面被滥用,比如委托可以用invoke调用,
但是事件只能在+=或-=的左侧,这样就增加了整个程序的安全性。
第二个问题:那委托和事件的关系什么样的呢?我们说事件是基于委托的。一方面,事件需要委托来做一个约束,这个约束规定了事件源发送什么要求给事件的订阅者,
事件订阅者的事件处理器必须和这个约束相对应才可以订阅这个事件,另一方面,事件订阅者收到事件以后做出事件处理器,而这个事件处理器必须通过委托才可以做到。
4、简单实例:
Example:做一个窗口,有文本框和按钮,点击按钮文本框显示时间,不用WindowsForms
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace ConsoleApp14 { class Program { public static TextBox textBox; public static Button button; static void Main(string[] args) { Form form = new Form(); TextBox textBox = new TextBox(); Button button = new Button(); form.Controls.Add(textBox); form.Controls.Add(button); textBox.Width = 400; button.Top = 100; button.Click += Button_Click; form.ShowDialog(); } private static void Button_Click(object sender, EventArgs e) { textBox.Text = DateTime.Now.ToString(); } } }
这里举的事例就是windowsforms内部的代码,我们说事件本身是不会发生的是由事件源内部的逻辑所触发,在本例中,并不是人按了按钮然后按钮触发了事件,
这其中还有一个小过程,就是当按钮被key down再key up时,向程序内部发送了一系列电讯号,通知电脑,然后再发生事件,
5、声明事件的相关约定:
用于声明事件的委托一般用:事件+EvnetHandler,参数一般有2个,第一个事件源,第二个EventArgs的派生类,用于触发事件的方法名一般为On+方法名,
访问级别Protected。可能有点蒙,举个实例就懂了。
Example:举一个顾客在KFC点餐的例子
namespace ConsoleApp15 { class Program { static void Main(string[] args) { Customer customer = new Customer(); Waitor waitor = new Waitor(); customer.Order += waitor.Serve; customer.Eat(); customer.Pay(); } } public delegate void OrderEventHandler(Customer customer, OrderEventArgs e); public class Customer { public int Money { get; set; } public event OrderEventHandler Order; public void Pay() { Console.WriteLine($"OK,{Money} dollars"); } public void Eat() { Console.WriteLine("Let's go to the KFC..."); Console.WriteLine("Stand in front of the waitor..."); Console.WriteLine("A hamburger,Please..."); OnOrder(); } protected void OnOrder() { OrderEventArgs orderEventArgs = new OrderEventArgs(); orderEventArgs.Snack = "Hamburger"; orderEventArgs.Size = "large"; this.Order.Invoke(this, orderEventArgs); } } public class OrderEventArgs : EventArgs { public string Snack { get; set; } public string Size { get; set; } } class Waitor { public void Serve(Customer customer, OrderEventArgs e) { Console.WriteLine($"Here is your snack {e.Snack}"); int price = 20; switch (e.Size) { case "large": price *= 2; break; case "small": price *= 1; break; default: break; } customer.Money += price; } } }
按照事件的五个要素,首先需要事件源,做一个Customer类,还需要一个事件订阅者,做一个Waitor类,然后根据订阅关系去写具体的方法,订阅关系customer.Order += waitor.Serve; Customer点餐Waitor服务,waitor类中上餐并算好价格,这个时候需要一个事件处理器OrderEventHandler,这个委托的参数需要一个OrderEventArgs,创建这个类写好属性,在写好委托和事件,然后在Customer类中写点餐事件,点餐事件为Protected的,和public型的委托字段一样防止被外界滥用,提高安全性。
想融会贯通其实也不难,只需要将事件的5个要素每一个列举出来,那么最后事件也就出来了。
至此事件总结完毕,有不明之处还请指教。 2018-08-17 16:43:19