回撥函式、訊息和事件例程 (轉)

worldblog發表於2007-12-09
回撥函式、訊息和事件例程 (轉)[@more@]

回撥、訊息和事件例程


  (calling)機制從時代起已經大量使用:準備一段現成的程式碼,呼叫者可以隨時跳轉至此段程式碼的起始地址,完後再返回跳轉時的後續地址。為此準備了現成的呼叫指令,呼叫時可以壓棧保護現場,呼叫結束後從堆疊中彈出現場地址,以便自動返回。借堆疊保護現場真是一項絕妙的發明,它使呼叫者和被調者可以互不相識,於是才有了後來的函式和構件,使吾輩者如此輕鬆愉快。若評選對人類影響最大之發明,在火與車輪之後,筆者當推壓棧呼叫。
  話雖這樣說,此呼叫機制並非完美。回撥函式就是一例。函式之類本是為呼叫者準備的美餐,其烹製者應對食客瞭如指掌,但實情並非如此。例如,寫一個排序函式供他人呼叫,其中必包含比較大小。麻煩來了:此時並不知要比較的是何類資料--整數、浮點數、字串?於是只好為每類資料製作一個不同的排序函式。更通行的辦法是在函式引數中列一個回撥函式地址,並通知呼叫者:君需自己準備一個比較函式,其中包含兩個指標類引數,函式要比較此二指標所指資料之大小,並由函式返回值說明比較結果。排序函式借此呼叫者提供的函式來比較大小,借指標傳遞引數,可以全然不管所比較的資料型別。被呼叫者回頭呼叫呼叫者的函式(夠咬嘴的),故稱其為回撥(callback)。
  回撥函式使結構亂了許多。 函式集中有不少回撥函式,儘管有詳盡說明,仍使初學者一頭霧水。恐怕這也是無奈之舉。無論何種事物,能以樹形結構單向描述畢竟讓人舒服些。如果某家族中孫輩又是某祖輩的祖輩,恐怕無人能理清其中的頭緒。但資料處理之複雜往往需要構成網狀結構,非簡單的客戶/關係能窮盡。
  Windows 還包含著另一種更為廣泛的回撥機制,即訊息機制。訊息本是 Windows 的基本控制手段,乍看與函式呼叫無關,其實是一種變相的函式呼叫。傳送訊息的目的是通知收方執行一段預先準備好的程式碼,相當於呼叫一個函式。訊息所附帶的 WParam 和 LParam 相當於函式的引數,只不過比普通引數更通用一些。應用程式可以主動傳送訊息,更多情況下是坐等 Windows 傳送訊息。一旦訊息進入所屬訊息佇列,便檢感興趣的那些,跳轉去執行相應的訊息處理程式碼。本是為應用程式服務,由應用程式來呼叫。而應用程式一旦啟動,卻要反過來等待作業系統的呼叫。這分明也是一種回撥,或者說是一種廣義回撥。其實,應用程式之間也可以形成這種回撥。假如程式 B 收到程式 A 發來的訊息,啟動了一段程式碼,其中又向程式 A 傳送訊息,這就形成了回撥。這種回撥比較隱蔽,弄不好會搞成遞迴呼叫,若缺少終止條件,將會迴圈不已,直至把程式搞垮。若是故意編寫成此遞迴呼叫,並設好終止條件,倒是很有意思。但這種程式結構太隱蔽,除非十分必要,還是不用為好。
  利用訊息也可以構成狹義回撥。上面所舉排序函式一例,可以把回撥函式地址換成視窗 handle。如此,當需要比較資料大小時,不是去呼叫回撥函式,而是借 API 函式 SendMessage 向指定視窗傳送訊息。收到訊息方負責比較資料大小,把比較結果透過訊息本身的返回值傳給訊息傳送方。所實現的功能與回撥函式並無不同。當然,此例中改為訊息純屬畫蛇添腳,反倒把程式搞得很慢。但其他情況下並非總是如此,特別是需要非同步呼叫時,傳送訊息是一種不錯的選擇。假如回撥函式中包含處理之類的低速處理,呼叫方等不得,需要把同步呼叫改為非同步呼叫,去啟動一個單獨的執行緒,然後馬上執行後續程式碼,其餘的事讓執行緒慢慢去做。一個替代辦法是借 API 函式 PostMessage 傳送一個非同步訊息,然後立即執行後續程式碼。這要比自己搞個執行緒省事許多,而且更。
  如今我們是活在一個 時代。只要與程式設計有關,無論何事都離不開 object。但 object 並未消除回撥,反而把它發揚光大,弄得到處都是,只不過大都以事件(event)的身份出現,鑲嵌在某個結構之中,顯得更正統,更容易被人接受。應用程式要使用某個構件,總要先弄清構件的屬性、方法和事件,然後給構件屬性賦值,在適當的時候呼叫適當的構件方法,還要給事件編寫處理例程,以備構件程式碼來呼叫。何謂事件?它不過是一個指向事件例程的地址,與回撥函式地址沒什麼區別。
  不過,此種回撥方式比傳統回撥函式要高明許多。首先,它把讓人不太舒服的回撥函式變成一種自然而然的處理例程,使程式設計者頓覺氣順。再者,地址是一個危險的東西,用好了可使程式加速,用不好處處是陷阱,程式隨時都會崩潰。現代程式設計方式總是想法把地址隱藏起來(隱藏比較徹底的如 和 ),其代價是降低了程式。事件例程使程式設計者無需直接操作地址,但並不會使程式減速。更妙的是,此一改變,本是有損程式結構之奇技怪巧變成一種嶄新設計理念,不僅免去被人抨擊,而且逼得吾等凡人淨手更衣,細細研讀,仰慕至今。只是偶然靜心思慮,發覺不過一瓶舊酒而已,故引得此番議論,讓諸君見笑了。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-989999/,如需轉載,請註明出處,否則將追究法律責任。

相關文章