自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

淺析C#事件注冊(cè)和注銷

開發(fā) 后端
本文是作者總結(jié)的關(guān)于C#事件注冊(cè)和注銷的相關(guān)問題的經(jīng)驗(yàn),希望對(duì)大家有所幫助。

由于.NET框架對(duì)消息循環(huán)機(jī)制進(jìn)行了很好的封裝,開發(fā)人員不再需要深入的了解Windows事件/消息實(shí)現(xiàn)的具體機(jī)制,也無需創(chuàng)建復(fù)雜的事件結(jié)構(gòu)體和所謂的消息句柄。我們所要做的無非就是——1、使用重載運(yùn)算符“+=”注冊(cè)一個(gè)事件;2、編寫對(duì)于該事件的處理方法?! ?

如此簡(jiǎn)單,以至于習(xí)慣了Win32編程的伙計(jì)們對(duì)此嗤之以鼻,諷之:“我們是開手排擋車的專業(yè)選手,你們.NET一族只能玩玩自動(dòng)檔。什么?你們還看《頭文字D》?能看懂嗎?”…  

不理他們!咱們說咱們的。轉(zhuǎn)頭前甩給他們一句話:“迂腐!”。如果不解恨,那么在引用一段名師的話:“我們從不樂意改變自己的工作習(xí)慣,就像把妻子的照片放在臺(tái)燈下面一樣。然而,當(dāng)一種新的方法確實(shí)能極大的提高我們的工作效率和行動(dòng)力時(shí),我們干嘛要固執(zhí)呢?”——夠效果了吧?  

言歸正傳?! ?

前幾天,我在編寫主窗體與子模塊的事件通信時(shí),遇到了一個(gè)極其堪稱郁悶的問題。說這個(gè)問題前,我和大家交代一下我的設(shè)計(jì)思路?! ?

C#事件注冊(cè)和注銷:設(shè)計(jì)思路

主窗體(frmMain :IParentForm)

事件成員:

  1. public event ParentEventHandler OnUserListCreated;   

事件處理方法:

  1. void ToDoOnRequestUserList(object sender, EventArgs e){   
  2.  
  3.    //創(chuàng)建DataTable dt   
  4.  
  5.    …   
  6.  
  7.    This.OnUserListCreated(thisnew ParentEventArgs(dt));   
  8.  
  9.   }   

某一行注冊(cè)子窗體事件:

  1.   frmChild.OnRequestUserList += new EventHandler (ToDoOnRequestUserList);     

子窗體(frmChild)

事件成員:

  1.   public event EventHandler OnUserListCreated;  

事件處理方法:

  1.   void ToDoOnRequestUserReturned(object sender, ParentEventArgs e){}  

在OnLoad事件處理方法中注冊(cè)主窗體的事件:

  1.   (this.MdiParent as IParentForm). OnUserListCreated += new ParentEventHandler (ToDoOnRequestUserReturned);    

主窗體對(duì)象為frmMain,它實(shí)現(xiàn)了IParentForm接口,該接口定義了事件成員OnUserListCreated(它的EventArgs為自定義的ParentEventArgs)。frmMain對(duì)象在某處創(chuàng)建了一個(gè)子窗體frmChild,并注冊(cè)了frmChild的事件OnRequestUserList?! ?

子窗體對(duì)象frmChild在載入時(shí)(OnLoad方法中)獲得frmMain的引用,并注冊(cè)了frmMain的事件OnUserListCreated?! ?

根據(jù)業(yè)務(wù)邏輯,子窗體運(yùn)行的某一時(shí)刻,用戶行為觸發(fā)了事件OnRequestUserList,此時(shí)frmMain將捕獲此事件并調(diào)用自身的處理方法生成一個(gè)被請(qǐng)求的用戶列表(DataTable)。然后,frmMain發(fā)出了事件OnUserListCreated以提示列表生成完畢,并將剛剛創(chuàng)建的DataTable作為ParentEventArgs參數(shù)插入事件中。隨后,子窗體將接收到這個(gè)事件,并在自己的事件處理方法中對(duì)傳來的DataTable進(jìn)行自己的業(yè)務(wù)邏輯動(dòng)作?! ?

在接下來程序的運(yùn)行中,可愛的代碼心情愉悅地順利執(zhí)行…但是,好景不長!  

C#事件注冊(cè)和注銷:遇到的問題

當(dāng)我將打開的子窗體關(guān)閉后再重新打開,主窗體在觸發(fā)OnUserListCreated事件后發(fā)生調(diào)用目標(biāo)異常,子窗體在該事件的處理方法中也拋出NullReferenceException異常(未將對(duì)象引用設(shè)置到對(duì)象實(shí)例)。當(dāng)我在子窗體的事件處理方法ToDoOnRequestUserReturned中設(shè)置斷點(diǎn)調(diào)試后發(fā)現(xiàn):所有的控件、變量都為null??!  

那叫郁悶,那叫惆悵…公車上、步行中、如廁時(shí)、入睡前,我估摸著這種靈異現(xiàn)象可能與最近隔壁鄰居家小貓的突然消失有著千絲萬縷的聯(lián)系…當(dāng)然,作為基督教徒的我,也后怕這是主,耶穌基督對(duì)于我大前天橫闖馬路的懲罰…  

無助中,我極其盲目的在frmChild的ToDoOnRequestUserReturned方法中加入了一行語句:“MessageBox.ShowDialog(“So boring a thing!”)”以發(fā)泄心情。保存、編譯、運(yùn)行——大壞蛋的面目露了出來!當(dāng)我***次打開子窗體的時(shí)候,如我所料,程序正常運(yùn)行并彈出了MessageBox。關(guān)鍵是,當(dāng)我關(guān)閉子窗口并第二次打開它執(zhí)行時(shí),MessageBox彈出了兩次!恩…  

帶著疑問,我重復(fù)了以上關(guān)閉、打開步驟,MessageBox彈出了三次!——事情已經(jīng)有了眉目。在我輾轉(zhuǎn)反復(fù)的思考后(也許有人會(huì)罵我菜鳥…),終于明白了所有事情的緣由:  

因?yàn)槌绦蛞恢碧幵谶\(yùn)行中,所以主窗體對(duì)象一直駐留內(nèi)存中并保持著自身的狀態(tài)(它沒有的disposed),所以,每次子窗體創(chuàng)建時(shí),主窗體都會(huì)注冊(cè)它的OnRequestUserList事件,同樣的,該子窗體在加載時(shí),自身也會(huì)把主窗體的OnUserListCreated事件注冊(cè)一次?! ?

問題就出在這里,雖然子窗體關(guān)閉了,并disposed了。但是,它關(guān)閉時(shí)并沒有把在主窗體注冊(cè)的事件同時(shí)注銷。隨著子窗體一次次的打開,主窗體的OnUserListCreated就被+=了N多了注冊(cè)用戶,其中的N-1個(gè)用戶其實(shí)早已經(jīng)不存在了,而主窗體全然不知。所以當(dāng)發(fā)出OnUserListCreated事件后,主窗體還會(huì)以無反顧地去調(diào)用這N多個(gè)方法代理,這必然會(huì)導(dǎo)致異常拋出——***打開的那個(gè)子窗體接受到一次次傳來的事件,并企圖調(diào)用ToDoOnUserListReturned方法,如果此方法中包含著對(duì)本對(duì)象成員變量的操作,自然會(huì)引出“未將引用設(shè)置到對(duì)象實(shí)例”的異常?! ?

也許有朋友會(huì)問,為什么主窗體調(diào)用那些早已disposed的frmChild的方法的代理時(shí),會(huì)被當(dāng)前存在的那個(gè)frmChild執(zhí)行呢?我認(rèn)為這可能是由于類實(shí)例的同一個(gè)方法在內(nèi)存棧中共享空間造成的;而成員變量在堆中存放,各自維護(hù)其狀態(tài),當(dāng)其所屬的對(duì)象被釋放回收時(shí),其值也就置為null了。(個(gè)人觀點(diǎn),望兄弟姐們給予指正)  

C#事件注冊(cè)和注銷:總結(jié) 

子窗體在關(guān)閉時(shí),應(yīng)當(dāng)把自己注冊(cè)的主窗體對(duì)象(或者是長久駐留內(nèi)存對(duì)象)事件一一注銷。例如本例中,應(yīng)在子窗體的OnClosed事件處理方法中加入以下代碼:

  1. (this.MdiParent as IParentForm). OnUserListCreated -= new ParentEventHandler (ToDoOnRequestUserReturned)  

如果僅僅是為了在主窗體執(zhí)行完某項(xiàng)操作后觸發(fā)子窗體某一方法的執(zhí)行,我們通常不采用事件機(jī)制,而采用以下兩種方法:

A. 將此方法訪問屬性改為public,然后由主窗體適時(shí)調(diào)用。

B. 定義一個(gè)接口,子窗體對(duì)象實(shí)現(xiàn)這個(gè)接口,并把該目標(biāo)方法提升為該接口的成員。由主窗體適時(shí)調(diào)用這個(gè)接口成員方法。

【編輯推薦】

  1. 學(xué)習(xí)C#消息:循序漸進(jìn)
  2. 解惑答疑:C#委托和事件
  3. 學(xué)習(xí)C#實(shí)現(xiàn)HTTP協(xié)議:多線程文件傳輸
  4. 進(jìn)一步接觸C#委托與事件
  5. 淺析四種C#轉(zhuǎn)換的區(qū)別
責(zé)任編輯:book05 來源: hi.baidu
相關(guān)推薦

2009-08-12 15:20:21

C#事件處理

2009-10-09 09:07:40

C#委托和事件

2009-09-07 04:19:56

C#窗體事件

2009-09-09 11:29:32

C# TextBox事

2009-09-10 12:00:09

C# listbox

2009-08-19 14:12:23

C#操作注冊(cè)表

2009-08-19 10:41:14

C# switch和c

2009-08-27 13:50:08

C# StringBu

2009-08-20 17:47:54

C#異步編程模式

2009-09-11 09:20:00

C# button事件

2009-08-31 18:34:57

C#接口事件

2009-09-07 05:31:39

C#窗體關(guān)閉事件

2009-09-02 19:11:42

C#鼠標(biāo)滾輪

2009-08-19 13:25:53

C#操作注冊(cè)表

2009-08-19 13:34:55

C#操作注冊(cè)表

2009-08-19 13:30:58

C#操作注冊(cè)表

2009-08-27 16:18:47

C#類C#結(jié)構(gòu)體

2009-08-26 09:54:45

C#打印預(yù)覽C#打印

2009-09-10 16:38:43

C# get set用

2009-08-07 17:25:37

C# SortedLi
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)