事件概述
委托是一种类型可以被实例化,而事件可以看作将多播委托进行封装的一个对象成员(简化委托调用列表增加和删除方法)但并非特殊的委托,保护订阅互不影响。
基础事件(event)
在.Net中声明事件使用关键词event,使用也非常简单在委托(delegate)前面加上event:
1 class Program 2 { 3 /// <summary> 4 /// 定义有参无返回值委托 5 /// </summary> 6 /// <param name="i"></param> 7 public delegate void NoReturnWithParameters(); 8 /// <summary> 9 /// 定义接受NoReturnWithParameters委托类型的事件 10 /// </summary> 11 static event NoReturnWithParameters NoReturnWithParametersEvent; 12 static void Main(string[] args) 13 { 14 //委托方法1 15 { 16 Action action = new Action(() => 17 { 18 Console.WriteLine("测试委托方法1成功"); 19 }); 20 NoReturnWithParameters noReturnWithParameters = new NoReturnWithParameters(action); 21 //事件订阅委托 22 NoReturnWithParametersEvent += noReturnWithParameters; 23 //事件取阅委托 24 NoReturnWithParametersEvent -= noReturnWithParameters; 25 } 26 //委托方法2 27 { 28 //事件订阅委托 29 NoReturnWithParametersEvent += new NoReturnWithParameters(() => 30 { 31 Console.WriteLine("测试委托方法2成功"); 32 }); 33 } 34 //委托方法3 35 { 36 //事件订阅委托 37 NoReturnWithParametersEvent += new NoReturnWithParameters(() => Console.WriteLine("测试委托方法3成功")); 38 } 39 //执行事件 40 NoReturnWithParametersEvent(); 41 Console.ReadKey(); 42 } 43 /* 44 * 作者:Jonins 45 * 出处:http://www.cnblogs.com/jonins/ 46 */ 47 }
上述代码执行结果:
事件发布&订阅
事件基于委托,为委托提供了一种发布/订阅机制。当使用事件时一般会出现两种角色:发行者和订阅者。
发行者(Publisher)也称为发送者(sender):是包含委托字段的类,它决定何时调用委托广播。
订阅者(Subscriber)也称为接受者(recevier):是方法目标的接收者,通过在发行者的委托上调用+=和-=,决定何时开始和结束监听。一个订阅者不知道也不干涉其它的订阅者。
来电->打开手机->接电话,这样一个需求,模拟订阅发布机制:
1 /// <summary> 2 /// 发行者 3 /// </summary> 4 public class Publisher 5 { 6 /// <summary> 7 /// 委托 8 /// </summary> 9 public delegate void Publication(); 10 11 /// <summary> 12 /// 事件 这里约束委托类型可以为内置委托Action 13 /// </summary> 14 public event Publication AfterPublication; 15 /// <summary> 16 /// 来电事件 17 /// </summary> 18 public void Call() 19 { 20 Console.WriteLine("显示来电"); 21 if (AfterPublication != null)//如果调用列表不为空,触发事件 22 { 23 AfterPublication(); 24 } 25 } 26 } 27 /// <summary> 28 /// 订阅者 29 /// </summary> 30 public class Subscriber 31 { 32 /// <summary> 33 /// 订阅者事件处理方法 34 /// </summary> 35 public void Connect() 36 { 37 Console.WriteLine("通话接通"); 38 } 39 /// <summary> 40 /// 订阅者事件处理方法 41 /// </summary> 42 public void Unlock() 43 { 44 Console.WriteLine("电话解锁"); 45 } 46 } 47 /* 48 * 作者:Jonins 49 * 出处:http://www.cnblogs.com/jonins/ 50 */
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 //定义发行者 6 Publisher publisher = new Publisher(); 7 //定义订阅者 8 Subscriber subscriber = new Subscriber(); 9 //发行者订阅 当来电需要电话解锁 10 publisher.AfterPublication += new Publisher.Publication(subscriber.Unlock); 11 //发行者订阅 当来电则接通电话 12 publisher.AfterPublication += new Publisher.Publication(subscriber.Connect); 13 //来电话了 14 publisher.Call(); 15 Console.ReadKey(); 16 } 17 }
执行结果:
注意:
1.事件只可以从声明它们的类中调用, 派生类无法直接调用基类中声明的事件。
1 publisher.AfterPublication();//这行代码在Publisher类外部调用则编译不通过
2.对于事件在声明类外部只能+=,-=不能直接调用,而委托在外部不仅可以使用+=,-=等运算符还可以直接调用。
下面调用方式与上面执行结果一样,利用了委托多播的特性。
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Publisher publisher = new Publisher(); 6 Subscriber subscriber = new Subscriber(); 7 //------利用多播委托------- 8 var publication = new Publisher.Publication(subscriber.Unlock); 9 publication += new Publisher.Publication(subscriber.Connect); 10 publisher.AfterPublication += publication; 11 //---------End----------- 12 publisher.Call(); 13 Console.ReadKey(); 14 } 15 }
自定义事件(EventArgs&EventHandler&事件监听器)
有过Windwos Form开发经验对下面的代码会熟悉:
1 private void Form1_Load(object sender, EventArgs e) 2 { 3 ... 4 }
在设计器Form1.Designer.cs中有事件的附加。这种方式属于Visual Studio IDE事件订阅。
1 this.Load += new System.EventHandler(this.Form1_Load);
在 .NET Framework 类库中,事件基于 EventHandler 委托和 EventArgs 基类。
基于EventHandler模式的事件:
1 /// <summary> 2 /// 事件监听器 3 /// </summary> 4 public class Consumer 5 { 6 private string _name; 7 8 public Consumer(string name) 9 { 10 _name = name; 11 } 12 public void Monitor(object sender, CustomEventArgs e) 13 { 14 Console.WriteLine($"Name:{_name}; 信息:{e.Message};到底要不要接呢?"); 15 } 16 } 17 /// <summary> 18 /// 定义保存自定义事件信息的对象 19 /// </summary> 20 public class CustomEventArgs : EventArgs//作为事件的参数,必须派生自EventArgs基类 21 { 22 public CustomEventArgs(string message) 23 { 24 this.Message = message; 25 } 26 public string Message { get; set; } 27 } 28 /// <summary> 29 /// 发布者 30 /// </summary> 31 public class Publisher 32 { 33 public event EventHandler<CustomEventArgs> Publication;//定义事件 34 public void Call(string w) 35 { 36 Console.WriteLine("显示来电." + w); 37 OnRaiseCustomEvent(new CustomEventArgs(w)); 38 } 39 //在一个受保护的虚拟方法中包装事件调用。 40 //允许派生类覆盖事件调用行为 41 protected virtual void OnRaiseCustomEvent(CustomEventArgs e) 42 { 43 //在空校验之后和事件引发之前。制作临时副本,以避免可能发生的事件。 44 EventHandler<CustomEventArgs> publication = Publication; 45 //如果没有订阅者,事件将是空的。 46 if (publication != null) 47 { 48 publication(this, e); 49 } 50 } 51 } 52 /// <summary> 53 /// 订阅者 54 /// </summary> 55 public class Subscriber 56 { 57 private string Name; 58 public Subscriber(string name, Publisher pub) 59 { 60 Name = name; 61 //使用c# 2.0语法订阅事件 62 pub.Publication += UnlockEvent; 63 pub.Publication += ConnectEvent; 64 } 65 //定义当事件被提起时该采取什么行动。 66 void ConnectEvent(object sender, CustomEventArgs e) 67 { 68 Console.WriteLine("