[C#1] 10-事件
事件概述
CLR的事件模型建立在委托的機制之上。定義事件成員的類型允許類型(或者類型的實例)在某些特定事件發(fā)生時通知其他對象,事件為類型提供了一下三種能力:
1允許對象登記該事件 2允許對象注銷該事件 3允許定義事件的對象維持一個登記對象的集合,并在某些特定的事件反生時通知這些對象下面是根據(jù)一個上課的場景解釋事件的原理【上課鈴響,老師講課,學生進教室聽課】。定義一個RingManager類管理上課鈴聲,定義一個SchoolBell【上課鈴響】的事件,Teacher和Student類型登記該事件。當Teacher和Student對象構(gòu)造時登記 SchoolBell事件,上課鈴聲響起時則通知這兩個對象。
發(fā)布事件
1 class RingManager 2 { 3 /* 4 * 定義一個類型保存發(fā)送給事件登記者的附加信息, 5 * 按照.NET框架的約定,所有這樣保存事件信息的類型 6 * 都繼承自System.EventArgs,且以EventArgs作 7 * 為名字的結(jié)尾。 8 */ 9 public class SchoolBellEventArgs : EventArgs 10 { 11 //教室位置 12 public readonly string classRoom; 13 //上課時間 14 public readonly DateTime schoolTime; 15 16 public SchoolBellEventArgs(string classroom, 17 DateTime schooltime) 18 { 19 this.classRoom = classroom; 20 this.schoolTime = schooltime; 21 } 22 } 23 24 /* 按照.NET框架的約定,委托類型的名稱應(yīng)該以 25 * EventHander結(jié)束,回調(diào)方法的 原型有一個void返回值 26 * 并且接受兩個參數(shù)【object指向發(fā)送通知的對象, 27 * e是給接受者的附加信息】 28 */ 29 public delegate void SchoolBellEventHandler 30 (object sender, SchoolBellEventArgs e); 31 32 //聲明事件成員 33 public event SchoolBellEventHandler SchoolBell; 34 35 //負責通知事件的登記對象 36 public void OnSchoolBell(SchoolBellEventArgs e) 37 { 38 if (SchoolBell != null) 39 { 40 SchoolBell(this, e); 41 } 42 } 43 //把輸入的參數(shù)轉(zhuǎn)化為事件調(diào)用 44 public void IsOnTimeToClass(string classRoom, 45 DateTime time) 46 { 47 OnSchoolBell(new SchoolBellEventArgs 48 (classRoom, time)); 49 } 50 } public event SchoolBellEventHandler SchoolBell;當編譯器遇到上面一行代碼時,會產(chǎn)生一下3個構(gòu)造[IL代碼]:
1 public event SchoolBellEventHandler SchoolBell 2 //雖然我們聲明的是public,但是編譯器為我們產(chǎn)生了private的 私有字段 3 //這樣做可以防止類型外的的錯誤操作[也說明事件在一方面封裝了委托] 4 .field private class RingManager/SchoolBellEventHandler SchoolBell 5 6 //add_SchoolBell方法注冊事件 7 .method public hidebysig specialname instance void 8 add_SchoolBell(class RingManager/SchoolBellEventHandler 9 'value') cil managed synchronized 10 { 11 //......省略 12 //調(diào)用System.Delegate的靜態(tài)方法Combine把委托對象添加到委托鏈表 13 IL_0008: call class[mscorlib]System.Delegate[mscorlib]System.Delegate:: 14 Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) 15 //..省略 16 } 17 18 //注銷事件 19 .method public hidebysig specialname instance void 20 remove_SchoolBell(class RingManager/SchoolBellEventHandler 21 'value') cil managed synchronized 22 { 23 //。。。省略 24 //調(diào)用System.Delegate的靜態(tài)方法Remove從委托鏈表中移除委托對象 25 IL_0008:callclass[mscorlib]System.Delegate[mscorlib]System.Delegate:: 26 Remove(class [mscorlib]System.Delegate,class [mscorlib]System.Delegate) 27 //。。。省略 28 }仔細看IL代碼的話會發(fā)現(xiàn)add_SchoolBell和remove_SchoolBell方法聲明后面有一個單詞synchronized【它表示這個方法加鎖,相當于不管哪一個線程1每次運行到這個方法時,都要檢查有沒有其它正在用這個方法的2線程,有的話要等正在使用這個方法的線程2(或者其他345線程)運行完這個方法后再運行此線程1沒有的話,直接運行,這個特性是 [MethodImplAttribute(MethodImplOptions.Synchronized)],所屬命名空間是System.Runtime.CompilerServices】,這保證了登記注銷事件時的線程安全性。add_SchoolBell和remove_SchoolBell方法的訪問級別取決于源代碼中聲明的事件的訪問級別。
除了上述3個構(gòu)造外,編譯器還會在托管模塊中的元數(shù)據(jù)產(chǎn)生一個事件定義條目,包含了一些標記和定義事件所使用的委托類型,并且有對add和remove方法的引用。
.event RingManager/SchoolBellEventHandler SchoolBell {.addon instance void RingManager::add_SchoolBell(class RingManager/SchoolBellEventHandler).removeon instance void RingManager::remove_SchoolBell(class RingManager/SchoolBellEventHandler) } // end of event RingManager::SchoolBell偵聽事件
+=和-=對應(yīng)這rm.add_SchoolBell和rm.remove_SchoolBell方法【編譯器幫我們調(diào)用這兩個方法】:
1 //老師類,學生類就不寫了 2 class Teacher 3 { 4 //構(gòu)造老師對象時穿進去RingManager 5 public Teacher(RingManager rm) 6 { 7 rm.SchoolBell += 8 new RingManager.SchoolBellEventHandler(rm_SchoolBell); 9 } 10 11 //老師的回調(diào)方法 12 private void rm_SchoolBell(object sender, 13 RingManager.SchoolBellEventArgs e) 14 { 15 //sender表示RingManager的對象 16 Console.WriteLine("上課鈴響了--我是老師:"); 17 //e是事件的附加信息 18 Console.WriteLine("在{0}上課,現(xiàn)在時間是{1}", 19 e.classRoom, e.schoolTime); 20 } 21 22 //注銷SchoolBell事件 23 public void UnRegister(RingManager rm) 24 { 25 RingManager.SchoolBellEventHandler rm_sbea = 26 new RingManager.SchoolBellEventHandler(rm_SchoolBell); 27 rm.SchoolBell -= rm_sbea; 28 } 29 }測試代碼:
static void Main() {RingManager rm = new RingManager();Teacher t = new Teacher(rm);//觸發(fā)事件,老師t的到通知,做他該做的事情rm.IsOnTimeToClass("教學樓320教室", DateTime.Now);//老師注銷該事件 t.UnRegister(rm);//再次觸發(fā)事件,這次老師t接受不到通知了rm.IsOnTimeToClass("教學樓320教室", DateTime.Now); }顯示控制事件注冊
有時候我們的程序是在單線程的環(huán)境下運行的,還需要頻繁的添加或者移除委托實例,則編譯器自動產(chǎn)生的add和remove方法就不夠理想了,而且加了線程同步保護使性能有所損傷。
C#編譯器允許我們顯示的實現(xiàn)add和remove方法,下面的代碼對RingManager做了些修改,顯示的實現(xiàn)了add和remove方法:
1 //聲明一個私有的委托字段 2 private SchoolBellEventHandler _SchoolBell; 3 4 //像是屬性一樣的寫法,add和remove都接受一個隱含參數(shù)value 5 public event SchoolBellEventHandler SchoolBell 6 { 7 add 8 { 9 //添加value(委托對象)到委托鏈表 10 _SchoolBell = (SchoolBellEventHandler) 11 Delegate.Combine(_SchoolBell, value); 12 } 13 remove 14 { 15 //從委托鏈表_SchoolBell中移除value(委托對象) 16 _SchoolBell = (SchoolBellEventHandler) 17 Delegate.Remove(_SchoolBell, value); 18 } 19 } 20 21 //負責通知事件的登記對象 22 public void OnSchoolBell(SchoolBellEventArgs e) 23 { 24 if (_SchoolBell != null) 25 { 26 _SchoolBell(this, e); 27 } 28 }把第一個RingManager中的public event SchoolBellEventHandler SchoolBell和public void OnSchoolBell(SchoolBellEventArgs e)方法換成上述代碼就可以了。上述代碼和第一個RingManager中的代碼編譯后的代碼的行為除了去掉了[MethodImplAttribute(MethodImplOptions.Synchronized)]特性之外完全相同。這里去除了線程安全的保障。
轉(zhuǎn)載于:https://www.cnblogs.com/linianhui/archive/2011/04/03/csharp1_event.html
總結(jié)
以上是生活随笔為你收集整理的[C#1] 10-事件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android_Snake
- 下一篇: 保存web.config文件(转载)