程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 談談C#中的事件注冊和注銷

談談C#中的事件注冊和注銷

編輯:關於C語言
由於.Net框架對消息循環機制進行了很好的封裝,開發人員不再需要深入的了解Windows事件/消息實現的具體機制,也無需創建復雜的事件結構體和所謂的消息句柄。我們所要做的無非就是——1、使用重載運算符“+=”注冊一個事件;2、編寫對於該事件的處理方法。(關於C#2.0中事件處理的相關介紹,請參閱我的文章:C#2.0的泛型代理和事件 :以一當百的快感 )   如此簡單,以至於習慣了Win32編程的伙計們對此嗤之以鼻,諷之:“我們是開手排擋車的專業選手,你們.Net一族只能玩玩自動檔。什麼?你們還看《頭文字D》?能看懂嗎?”   不理他們!咱們說咱們的。轉頭前甩給他們一句話:“迂腐!”。如果不解恨,那麼在引用一段名師的話:“我們從不樂意改變自己的工作習慣,就像把妻子的照片放在台燈下面一樣。然而,當一種新的方法確實能極大的提高我們的工作效率和行動力時,我們干嘛要固執呢?”——夠效果了吧?   言歸正傳。   前幾天,我在編寫主窗體與子模塊的事件通信時,遇到了一個極其堪稱郁悶的問題。說這個問題前,我和大家交代一下我的設計思路。

主窗體(frmMain IParentForm) 事件成員:
public event ParentEventHandler OnUserListCreated;   事件處理方法: void ToDoOnRequestUserList(object sender, EventArgs e){          //創建DataTable dt          …          This.OnUserListCreated(this, new ParentEventArgs(dt)); }   某一行注冊子窗體事件: frmChild.OnRequestUserList += new EventHandler (ToDoOnRequestUserList);

子窗體(frmChild) 事件成員:
public event EventHandler OnUserListCreated;   事件處理方法: void ToDoOnRequestUserReturned(object sender, ParentEventArgs e){}   OnLoad事件處理方法中注冊主窗體的事件: (this.MdiParent as IParentForm). OnUserListCreated += new ParentEventHandler (ToDoOnRequestUserReturned);

  主窗體對象為frmMain,它實現了IParentForm接口,該接口定義了事件成員OnUserListCreated(它的EventArgs為自定義的ParentEventArgs)frmMain對象在某處創建了一個子窗體frmChild,並注冊了frmChild的事件OnRequestUserList   子窗體對象frmChild在載入時(OnLoad方法中)獲得frmMain的引用,並注冊了frmMain的事件OnUserListCreated   根據業務邏輯,子窗體運行的某一時刻,用戶行為觸發了事件OnRequestUserList,此時frmMain將捕獲此事件並調用自身的處理方法生成一個被請求的用戶列表(DataTable)。然後,frmMain發出了事件OnUserListCreated以提示列表生成完畢,並將剛剛創建的DataTable作為ParentEventArgs參數插入事件中。隨後,子窗體將接收到這個事件,並在自己的事件處理方法中對傳來的DataTable進行自己的業務邏輯動作。   在接下來程序的運行中,可愛的代碼心情愉悅地順利執行但是,好景不長!   當我將打開的子窗體關閉後再重新打開,主窗體在觸發OnUserListCreated事件後發生調用目標異常,子窗體在該事件的處理方法中也拋出NullReferenceException異常(未將對象引用設置到對象實例)。當我在子窗體的事件處理方法ToDoOnRequestUserReturned中設置斷點調試後發現:所有的控件、變量都為null!!   那叫郁悶,那叫惆怅公車上、步行中、如廁時、入睡前,我估摸著這種靈異現象可能與最近隔壁鄰居家小貓的突然消失有著千絲萬縷的聯系當然,作為基督教徒的我,也後怕這是主,耶稣基督對於我大前天橫闖馬路的懲罰   無助中,我極其盲目的在frmChildToDoOnRequestUserReturned方法中加入了一行語句:“MessageBox.ShowDialog(“So boring a thing!”)”以發洩心情。保存、編譯、運行——大壞蛋的面目露了出來!當我第一次打開子窗體的時候,如我所料,程序正常運行並彈出了MessageBox。關鍵是,當我關閉子窗口並第二次打開它執行時,MessageBox彈出了兩次!恩   帶著疑問,我重復了以上關閉、打開步驟,MessageBox彈出了三次!——事情已經有了眉目。在我輾轉反復的思考後(也許有人會罵我菜鳥),終於明白了所有事情的緣由:   因為程序一直處在運行中,所以主窗體對象一直駐留內存中並保持著自身的狀態(它沒有的disposed),所以,每次子窗體創建時,主窗體都會注冊它的OnRequestUserList事件,同樣的,該子窗體在加載時,自身也會把主窗體的OnUserListCreated事件注冊一次。   問題就出在這裡,雖然子窗體關閉了,並disposed了。但是,它關閉時並沒有把在主窗體注冊的事件同時注銷。隨著子窗體一次次的打開,主窗體的OnUserListCreated就被+=N多了注冊用戶,其中的N-1個用戶其實早已經不存在了,而主窗體全然不知。所以當發出OnUserListCreated事件後,主窗體還會以無反顧地去調用這N多個方法代理,這必然會導致異常拋出——唯一打開的那個子窗體接受到一次次傳來的事件,並企圖調用ToDoOnUserListReturned方法,如果此方法中包含著對本對象成員變量的操作,自然會引出“未將引用設置到對象實例”的異常。   也許有朋友會問,為什麼主窗體調用那些早已disposedfrmChild的方法的代理時,會被當前存在的那個frmChild執行呢?我認為這可能是由於類實例的同一個方法在內存棧中共享空間造成的;而成員變量在堆中存放,各自維護其狀態,當其所屬的對象被釋放回收時,其值也就置為null了。(個人觀點,望兄弟姐們給予指正)   綜上,我做一下總結:   子窗體在關閉時,應當把自己注冊的主窗體對象(或者是長久駐留內存對象)事件一一注銷。例如本例中,應在子窗體的OnClosed事件處理方法中加入以下代碼:
(this.MdiParent as IParentForm). OnUserListCreated  -=  new ParentEventHandler    (ToDoOnRequestUserReturned)   如果僅僅是為了在主窗體執行完某項操作後觸發子窗體某一方法的執行,我們通常不采用事件機制,而采用以下兩種方法:

A. 將此方法訪問屬性改為public,然後由主窗體適時調用。
B. 定義一個接口,子窗體對象實現這個接口,並把該目標方法提升為該接口的成員。由主窗體適時調用這個接口成員方法。  
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved