不少程序都有一个共同的需求,既当一个特定的程序事件发生时,程序的其余部分能够获得该事件已经发生的通知。
发布者/订阅者模式(publisher/subscriber pattern)能够知足这种需求。html
前一章介绍了委托。事件的不少部分都与委托相似。实际上,事件就像专门用于特殊用途的简单委托。事件包含了一个私有的委托。
有关事件的私有委托须要了解的重要事项以下:编程
须要在事件中使用的代码有5部分。并发
发布者类必须提供事件对象。建立事件比较简单–只须要委托类型和名字。事件声明的语法以下代码所示。
代码中声明了CountADozen事件。框架
class Incrementer { 关键字 委托类型 事件名 ↓ ↓ ↓ public event EventHandler CountedADozen; ... }
能够经过使用逗号分隔同时声明多个事件。异步
public event EventHandler MyEvent1,MyEvent2,OtherEvent;
还可使用static关键字让事件变成静态函数
public static event EventHandler CountedADozen;
事件是成员
一个常见误解是把事件认为是类型。和方法、属性同样,事件是类或结构的成员,这一点引出几个重要特性。this
事件声明须要委托类型的名字,咱们能够声明一个委托类型或使用已存在的。若是咱们声明一个委托类型,它必须指定事件保存的方法的签名和返回类型。
BCL(Base Class Library,基类库)声明了一个叫作EventHandler的委托,专门用于系统事件。设计
例:为CountedADozen事件增长3个方法。3d
incrementer.CountedADozen+=IncrementDozensCount;//实例方法 incrementer.CountedADozen+=ClassB.CounterHandlerB;//静态方法 mc.CountedADozen+=new EventHandler(cc.CounterHandlerC);//委托形式
例:Lambda表达式和匿名方法code
incrementer.CountedADozen+=()=>DozensCount++;//Lambda表达式 incrementer.CountedADozen+=delegate{DozensCount++;};//匿名方法
事件成员自己只保存了须要被调用的事件处理程序。若是事件没触发,什么都不会发生。咱们须要确保在合适的时候有代码来作这件事情。
例:下面代码触发了CountedADozen事件。
if(CountedADozen!=null) { CountedADozen(source,args); }
把事件声明和触发事件的代码放在一块儿便有了以下的发布者类声明。
下面展现了整个程序,代码须要注意的地方以下:
delegate void Handler(); //声明委托 //发布者 class Incrementer { public event Handler CountedADozen;//建立事件并发布 void DoCount(object source,EventArgs args) { for(int i=1;i<100;i++) { if((i%12==0)&&(CountedADozen!=null)) { CountedADozen(source,args); } } } } //订阅者 class Dozens { public int DozensCount{get;private set;} public Dozens(Incrementer incrementer) { DozensCount=0; incrementer.CountedADozen+=IncrementDozensCount;//订阅事件 } void IncrementDozensCount()//声明事件处理程序 { DozensCount++; } } class Program { static void Main() { var incrementer=new Incrementer(); var dozensCounter=new Dozens(incrementer); incrementer.DoCount(); Console.WriteLine("Number of dozens = {0}",dozensCounter.DozensCount); } }
GUI编程是事件驱动的,即程序运行时,它能够在任什么时候候被事件打断,好比鼠标点击,按下按键或系统定时器。在这些状况发生时,程序须要处理事件而后继续其余事情。
程序事件的异步处理是使用C#事件的绝佳场景。Windows GUI编程普遍的使用事件,对于事件的使用,.NET框架提供了一个标准模式。事件使用的标准模式根本就是System命名空间声明的EventHandler委托类型。EventHandler委托类型的声明代码以下。
public delegate void EventHandler(object sender,EventArgs e);
EventHandler委托类型的第二个参数是EventArgs类的UI项,它声明在System命名空间中。既然第二个参数用于传递数据,你可能会误认为EventArgs类的对象应该能够保存一些类型的数据。
例:Incrementer+EventHandler
public delegate void EventHandler(object sender,EventArgs e); //发布者 class Incrementer { public event EventHandler CountedADozen;//建立事件并发布 public void DoCount() { for(int i=1;i<100;i++) { if((i%12==0)&&(CountedADozen!=null)) { CountedADozen(this,null); } } } } //订阅者 class Dozens { public int DozensCount{get;private set;} public Dozens(Incrementer incrementer) { DozensCount=0; incrementer.CountedADozen+=IncrementDozensCount;//订阅事件 } void IncrementDozensCount(object source,EventArgs e)//声明事件处理程序 { DozensCount++; } } class Program { static void Main() { var incrementer=new Incrementer(); var dozensCounter=new Dozens(incrementer); incrementer.DoCount(); Console.WriteLine("Number of dozens = {0}",dozensCounter.DozensCount); } }
为了向EventArgs传入数据,而且符合标准惯例,咱们须要声明一个派生自EventArgs的自定义类,用于保存须要传入的数据。类的名称应该以EventArgs结尾。
例:声明自定义的EventArgs类,它将字符串存储在IterationCount字段中。
public class IncrementerEventArgs:EventArgs { public int IterationCount{get;set;} }
除了自定义类外,你还须要一个使用自定义类的委托类型。要获取该类,可使用泛型(第17章)版本的委托EventHandler<>。要使用泛型委托,须要作到如下两点:
例:使用了自定义类和自定义委托的事件示例
public class IncrementerEventArgs:EventArgs { public int IterationCount{get;set;} } //发布者 class Incrementer { 使用自定义类的泛型委托 ↓ public event EventHandler<IncrementerEventArgs> CountedADozen;//建立事件并发布 public void DoCount() { IncrementerEventArgs args=new IncrementerEventArgs(); for(int i=1;i<100;i++) { if((i%12==0)&&(CountedADozen!=null)) { args.IterationCount=i; CountedADozen(this,args); } } } } //订阅者 class Dozens { public int DozensCount{get;private set;} public Dozens(Incrementer incrementer) { DozensCount=0; incrementer.CountedADozen+=IncrementDozensCount;//订阅事件 } void IncrementDozensCount(object source,IncrementerEventArgs e)//声明事件处理程序 { Console.WriteLine("Incremented at iteration: {0} in {1}",e.IterationCount,source.ToString()); DozensCount++; } } class Program { static void Main() { var incrementer=new Incrementer(); var dozensCounter=new Dozens(incrementer); incrementer.DoCount(); Console.WriteLine("Number of dozens = {0}",dozensCounter.DozensCount); } }
用完事件处理程序后,可使用-=运算符把事件处理程序从事件中移除。
例:移除事件处理程序示例
class Publisher { public event EventHandler SimpleEvent; public void RaiseTheEvent(){SimpleEvent(this,null);} } class Subscriber { public void MethodA(object o,EventArgs e) { Console.WriteLine("AAA"); } public void MethodB(object o,EventArgs e) { Console.WriteLine("BBB"); } } class Program { static void Main() { var p=new Publisher(); var s=new Subscriber(); p.SimpleEvent+=s.MethodA; p.SimpleEvent+=s.MethodB; p.RaiseTheEvent(); Console.WriteLine("\r\nRemove MethodB"); p.SimpleEvent-=s.MethodB; p.RaiseTheEvent(); } }
若是一个处理程序向事件注册了屡次,那么移除程序时,将只移除列表中该处理程序的最后一个实例。
以前我提到+=和-=运算符是事件容许的惟一运算符。看到这里咱们应该知道,这些运算符有预约义行为。
咱们能够修改这些运算符的行为,而且使用它们时可让事件执行任何咱们但愿的自定义代码。但这是高级主题,此处只作简单介绍。
要改变这两个运算符的操做,能够为事件定义事件访问器。
例:具备访问器的事件声明。两个访问器都有隐式值参数value,它接受实例或静态方法的引用。
public event EventHandler CountedADozen { add { ... //执行+=运算符的代码 } remove { ... //执行-=运算符的代码 } }
声明事件访问器后,事件不包含任何内嵌委托对象。咱们必须实现本身的机制来存储和移除事件注册方法。
事件访问器表现为void方法,即没有返回值。
from: http://www.cnblogs.com/moonache/p/6340222.html