C# 事件(Event): 基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些提示信息,如系统生成的通知。应用程序需要在事件发生时响应事件。
事件是委托的一个实例。
事件需要在类中声明。
事件使用 发布-订阅(publisher-subscriber) 模型。
发布器(publisher) 是一个包含事件和委托定义的对象。事件和委托之间的联系也定义在这个对象中。发布器(publisher)类的对象调用这个事件,并通知其他的对象。
订阅器(subscriber) 是一个接受事件并提供事件处理程序的对象。在发布器(publisher)类中的委托调用订阅器(subscriber)类中的方法(事件处理程序)。
事件一般分为两种:
带参数传递的
不带参数传递的
基于上述的理解,实现 .Net 框架中的事件需要三个步骤。
实现事件的发布类
实现事件的订阅类
实现发布和订阅的关联(类似Qt中的信号关联)。
注意:
发布类里应当实现: 1、事件的触发(类似Qt中的发送信号emit signal)); 2、事件的引发方法
订阅类中应当实现: 事件接收时的操作,(Qt 中信号槽Slot)
现在来看下面的两个例子,如下:
1、不带参数传递的事件
namespace SimpleEvent { /***********发布器类***********/ public class EventTest { private int value; //定义一个参数 public delegate void NumManipulationHandler(); public event NumManipulationHandler ChangeNum; protected virtual void OnNumChanged() { if (ChangeNum != null) { ChangeNum(); /* 事件被触发 */ } else { Console.WriteLine("event not triggered"); Console.ReadKey(); /* 回车继续 */ } } public EventTest() //构造函数 { int n = 5; SetValue(n); //将值设置为5 } public void SetValue(int n) { // value = Console.ReadKey(); if (value != n) { value = n; OnNumChanged(); //当前后的值放生变化的时候触发事件 } } } /***********订阅器类***********/ public class subscribEvent { public void printf() { Console.WriteLine("event has been triggered"); Console.ReadKey(); /* 回车继续 */ } } /***********触发***********/ public class MainClass { public static void Main() { EventTest e = new EventTest(); /* 实例化发布者对象,第一次没有触发事件,屏幕上显示“event not triggered" */ subscribEvent v = new subscribEvent(); /* 实例化订阅者对象 */ e.ChangeNum += new EventTest.NumManipulationHandler(v.printf); /* 注册 */ e.SetValue(7); //第二次触发事件,屏幕上显示event has been triggered e.SetValue(11); //第三次触发事件,屏幕上显示event has been triggered } } }2、带参数传递的
namespace EventDemo { /// <summary> /// 发布事件的类 /// </summary> public class TestEventSource { //定义事件参数类 public class TestEventArgs : EventArgs { public readonly char KeyToRaiseEvent; //键盘值; public TestEventArgs(char keyToRaiseEvent) { KeyToRaiseEvent = keyToRaiseEvent; } } //定义delegate public delegate void TestEventHandler(object sender, TestEventArgs e); //sender:事件发布者;e:传递事件的参数 //用event 关键字声明事件对象 public event TestEventHandler TestEvent; //事件触发方法 protected virtual void OnTestEvent(TestEventArgs e) { if (TestEvent != null) TestEvent(this, e); // 触发事件;发布者不为空,即触发事件 } //引发事件 public void RaiseEvent(char keyToRaiseEvent) { TestEventArgs e = new TestEventArgs(keyToRaiseEvent); OnTestEvent(e); } } } namespace EventDemo { /// <summary> /// 监听事件的类 /// </summary> public class TestEventListener { //定义处理事件的方法,他与声明事件的delegate具有相同的参数和返回值类型 public void KeyPressed(object sender, TestEventSource.TestEventArgs e) { Console.WriteLine("发送者:{0},所按得健为:{1}", sender, e.KeyToRaiseEvent); } //订阅事件 public void Subscribe(TestEventSource evenSource) { evenSource.TestEvent += new TestEventSource.TestEventHandler(KeyPressed); //将发布者的testEvent 方法与订阅者的KeyPressed方法关联 } //取消订阅事件 public void UnSubscribe(TestEventSource evenSource) { evenSource.TestEvent -= new TestEventSource.TestEventHandler(KeyPressed); } } } namespace EventDemo { class Program { static void Main(string[] args) { //创建事件源对象 TestEventSource es = new TestEventSource(); //创建监听对象 TestEventListener el = new TestEventListener(); //订阅事件 Console.WriteLine("订阅事件\n"); el.Subscribe(es); //引发事件 Console.WriteLine("输入一个字符,再按enter键"); string s = Console.ReadLine(); es.RaiseEvent(s.ToCharArray()[0]); //取消订阅事件 Console.WriteLine("\n取消订阅事件\n"); el.UnSubscribe(es); //引发事件 Console.WriteLine("输入一个字符,再按enter健"); s = Console.ReadLine(); es.RaiseEvent(s.ToCharArray()[0]); } } }在上述例子中:
TestEventSource类。他就相当于windows控件类一样,是事件的源,里面包含有事件的声明,以及存储调用参数的事件参数类,以及事件的触发方法。
TestEventListener类。他提供了事件处理程序,并实现了事件处理程序和事件对象的邦定。
Test 类,实例化自定义事件的事件源对象,并调用 TestEventListener类中的Subscribe(es);方法进行事件对象和事件处理程序的邦定(订阅事件),调用 TestEventSource类中的RaiseEvent(char keyToRaiseEvent)引发对象,并有对象所指定的委托回调处理事件。完成整个自定义事件。
其中 RaiseEvent(char keyToRaiseEvent) 就相当于main()一样是自定义事件的执行入口, 从这个法开始—〉调用事件委托----〉查找订阅事件程序找到事件所封装的方法集----〉由委托回调事件处理程序并传递参数—〉执行事件处理程序。