标签:
我也人模人样的写一些笔记 帮助自己这个大菜鸟记得更牢
先解释下几个概念:
委托:是用来存储方法的类型 委托变量可以传递 使得方法也可以像变量一样传递。所以类中的事件成员也是委托类型=>事件要存储方法。
订阅事件:指的是向事件提供类的实例的事件(public)登记(+=)自己的回调方法,称为订阅事件,一旦事件发生,回调方法被执行,回调方法可以按照自己的方式处理来自事件的参数(sender,e),称为接受来自事件的通知。
1、事件应该能够传递事件的信息 所以要定义一个类型来包容要发送给事件接受者(就是给那些回调方法用的参数);规则要从EventArgs派生,如果不传递信息 用空的EventArgs就行 静态方法EventArgs.Empty()返回一个空的对象。
//第一步 定义事件的事件参数(即事件的附加信息)类型 写成嵌套类 /// <summary> /// 事件参数的类 /// </summary> internal class NewMailEventArgs : EventArgs { private readonly string m_from, m_to, m_subject;//只读的私有字段 ,不能修改 //只读的只能在构造器中初始化 public NewMailEventArgs(string from,string to,string subject) { this.m_from = from; this.m_subject = subject; this.m_to = to; } //当然 三个属性也是只读的 public string From { get { return m_from; } } public string To { get { return m_to; } } public string Sbuject { get { return m_subject; } } }
2、在事件提供类中定义事件成员 规则:public 关键字event 委托类型EventHandler<TEventArgs>的泛型参数就是要处理的事件信息的类型。event关键字的意义就是告诉编译器实现这个事件,为此编译器会生成一个私有的初始化为null的委托字段(这就是用来保存回调方法的委托列表),一个public的add方法和一个同样为public的remove方法,用这三个构造还维护一个事件。注意这两个方法返回的都是新的委托列表引用,字段会存储新的引用(列表头),类似于string。
//第二部 定义类的事件成员 //访问修饰符一定是public的 EventHandler这个泛型委托 的类型参数就是事件参数的类型 /// <summary> /// 定义事件成员 /// </summary> public event EventHandler<NewMailEventArgs> NewMail;
3、定义负责引发事件的方法来通知事件的登记对象 目的:事件在事件提供类的外面只能用+= 和-=来使用,无法直接调用。所以要定义一个可供派生类重写的方法。并出于线程安全 检查事件非空 传递给事件的是定义的事件附加信息e
//第三步 定义引发事件的方法 protected virtual void OnNewMail(NewMailEventArgs e)//虚方法是为了能够让派生类重写 protected是为了不能在类的外部调用 { //EventHandler<NewMailEventArgs> temp = Volatile.Read(ref NewMail); EventHandler<NewMailEventArgs> temp = NewMail;//将这个事件的引用直接赋值给temp if (temp != null) { temp(this, e);//这是Richter引发事件的方法,除了检查非空,还要处于线程安全 先复制到一个temp中 } }
4、定义一个公开的方法 将输入转化为事件信息e 然后调用上一步的方法。我感觉可以把第四步和第三步合起来 写一个public virual 获取输入,检查线程安全和非空的方法。是不是这样会破坏封装?
//第四步 定义方法将输入转化为期望事件 //由于引发事件的方法是protected的,所以必须还要有一个方法来公开它 并且该方法的参数应该是能够构造事件的参数 public void SimulateNewMail(string from, string to, string subject) { //不可以直接传递一个空的e给事件 所以要用附加信息构造e NewMailEventArgs e = new NewMailEventArgs(from, to, subject); OnNewMail(e); }
5、设计侦听事件的类型来订阅事件 在事件发生时 调用自己实现方法(自己的方法,和对事件发出者和事件信息进行处理)的回调方法
该类要实例化一个事件提供类的实例,向该实例+=登记自己的方法 在VS中 写上方法名 会自动帮你完成。要写一个注销的方法,不然一旦订阅了事件,就不能被GC。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 第十一章_事件 { /// <summary> //传真机类 /// </summary> class Fax { //该传真机类的构造器应该能够向事件登记自己的回调方法 public Fax( MailManager mm ) { mm.NewMail += FaxMsg; } private void FaxMsg(object sender, MailManager.NewMailEventArgs e) { MailManager m = (MailManager)sender; Console.WriteLine(m.ToString());//打印出事件发出者 Console.WriteLine(e.From+" "+e.To+" "+e.Sbuject); } //写一个注销对事件关注的方法 public void Unregister(MailManager mm) { mm.NewMail -= FaxMsg; } } }
这就是一个完整事件的订阅—发布模式。就是事件提供类的实例发动事件,订阅了事件的类提供的回调函数都执行的机制。
思考的不够深入,还需要更多的看代码写代码才能理解更深。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 第十一章_事件 { class Program { static void Main(string[] args) { MailManager mm = new MailManager(); Fax fax = new Fax(mm); mm.NewMail += myLetter; mm.SimulateNewMail("北京","厦门","一种遥远的致意,我的朋友"); Console.ReadKey(); } private static void myLetter(object sender, MailManager.NewMailEventArgs e) { Console.WriteLine("来自{0},发往{1},带去{2}的问候",e.From,e.To,e.Sbuject); } }
标签:
原文地址:http://www.cnblogs.com/plantprotecter/p/4999443.html