10->>事件
时间:2011-04-03 来源:乱舞春秋
1>=概述:
CLR的事件模型建立在委托的机制之上。定义事件成员的类型允许类型(或者类型的实例)在某些特定事件发生时通知其他对象,事件为类型提供了一下三种能力:1允许对象登记该事件;2允许对象注销该事件;3允许定义事件的对象维持一个登记对象的集合,并在某些特定的事件反生时通知这些对象。
一下是根据一个上课的场景解释事件的原理【上课铃响,老师讲课,学生进教室听课】。定义一个RingManager类管理上课铃声,定义一个SchoolBell【上课铃响】的事件,Teacher和Student类型登记该事件。当Teacher和Student对象构造时登记 SchoolBell事件,上课铃声响起时则通知这两个对象。
2>=发布事件:
RingManager1 class RingManager
2 {
3 /*
4 * 定义一个类型保存发送给事件登记者的附加信息,按照.NET框架的约定,
5 * 所有这样保存事件信息的类型都继承自System.EventArgs,且以EventArgs作
6 * 为名字的结尾。
7 */
8 public class SchoolBellEventArgs : EventArgs
9 {
10 //教室位置
11 public readonly string classRoom;
12 //上课时间
13 public readonly DateTime schoolTime;
14
15 public SchoolBellEventArgs(string classroom, DateTime schooltime)
16 {
17 this.classRoom = classroom;
18 this.schoolTime = schooltime;
19 }
20 }
21
22 /* 按照.NET框架的约定,委托类型的名称应该以EventHander结束,回调方法的
23 * 原型有一个void返回值,并且接受两个参数【object指向发送通知的对象,
24 * e是给接受者的附加信息】
25 */
26 public delegate void SchoolBellEventHandler(object sender, SchoolBellEventArgs e);
27
28 //声明事件成员
29 public event SchoolBellEventHandler SchoolBell;
30
31 //负责通知事件的登记对象
32 public void OnSchoolBell(SchoolBellEventArgs e)
33 {
34 if (SchoolBell != null)
35 {
36 SchoolBell(this, e);
37 }
38 }
39 //把输入的参数转化为事件调用
40 public void IsOnTimeToClass(string classRoom, DateTime time)
41 {
42 OnSchoolBell(new SchoolBellEventArgs(classRoom, time));
43 }
44 }
public event SchoolBellEventHandler SchoolBell;
当编译器遇到上面一行代码时,会产生一下3个构造<IL代码>:
public event SchoolBellEventHandler SchoolBell1 //虽然我们声明的SchoolBell字段是public的,但是编译器会把它编译为私有的字段
2 //这样可以防止类型外的代码错误的操作该字段[也说名事件对委托进行了封装操作]
3 .field private class RingManager/SchoolBellEventHandler SchoolBell
4
5 //add_SchoolBell方法,通过此共有方法其他对象可以登记事件
6 .method public hidebysig specialname instance void
7 add_SchoolBell(class RingManager/SchoolBellEventHandler 'value') cil managed synchronized
8 {
9 // 代码大小 24 (0x18)
10 .maxstack 8
11 IL_0000: ldarg.0
12 IL_0001: ldarg.0
13 IL_0002: ldfld class RingManager/SchoolBellEventHandler RingManager::SchoolBell
14 IL_0007: ldarg.1
15 //这里看出是调用System.Delegate的静态方法Combine将委托对象加到委托链表上
16 IL_0008: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
17 class [mscorlib]System.Delegate)
18 IL_000d: castclass RingManager/SchoolBellEventHandler
19 IL_0012: stfld class RingManager/SchoolBellEventHandler RingManager::SchoolBell
20 IL_0017: ret
21 } // end of method RingManager::add_SchoolBell
22
23 //remove_SchoolBell方法,注销事件
24 .method public hidebysig specialname instance void
25 remove_SchoolBell(class RingManager/SchoolBellEventHandler 'value') cil managed synchronized
26 {
27 // 代码大小 24 (0x18)
28 .maxstack 8
29 IL_0000: ldarg.0
30 IL_0001: ldarg.0
31 IL_0002: ldfld class RingManager/SchoolBellEventHandler RingManager::SchoolBell
32 IL_0007: ldarg.1
33 //通过System.Delegate的静态方法Remove将委托实例从委托链上移除
34 IL_0008: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate,
35 class [mscorlib]System.Delegate)
36 IL_000d: castclass RingManager/SchoolBellEventHandler
37 IL_0012: stfld class RingManager/SchoolBellEventHandler RingManager::SchoolBell
38 IL_0017: ret
39 } // end of method RingManager::remove_SchoolBell
仔细看IL代码的话会发现add_SchoolBell和remove_SchoolBell方法声明后面有一个单词<synchronized>【它表示这个方法加锁,相当于不管哪一个线程1每次运行到这个方法时,都要检查有没有其它正在用这个方法的2线程,有的话要等正在使用这个方法的线程2(或者其他345线程)运行完这个方法后再运行此线程1没有的话,直接运行,这个特性是 [MethodImplAttribute(MethodImplOptions.Synchronized)],所属命名空间是System.Runtime.CompilerServices】,这保证了登记注销事件时的线程安全性。add_SchoolBell和remove_SchoolBell方法的访问级别取决于源代码中声明的事件的访问级别。
除了上述3个构造外,编译器还会在托管模块中的元数据产生一个事件定义条目,包含了一些标记和定义事件所使用的委托类型,并且有对add和remove方法的引用。
View Code1 .event RingManager/SchoolBellEventHandler SchoolBell
2 {
3 .addon instance void RingManager::add_SchoolBell(class RingManager/SchoolBellEventHandler)
4 .removeon instance void RingManager::remove_SchoolBell(class RingManager/SchoolBellEventHandler)
5 } // end of event RingManager::SchoolBell
3>=侦听事件:
未完待续。。。