引言
<a></a>
本篇文章将為你介紹一下 Delegate 的使用方式,逐漸揭開 C# 當中事件(Event)的由來,它能使處理委托類型的過程變得更加簡單。
還将為您解釋委托的協變與逆變,以及如何使用 Delegate 使 Observer(觀察者)模式的使用變得更加簡單。
目錄
<a href="http://blog.51cto.com/2689556/843489#a1">一、委托類型的來由</a>
<a href="http://blog.51cto.com/2689556/843489#a2">二、建立委托類</a>
<a href="http://blog.51cto.com/2689556/843489#a3">三、委托使用方式</a>
<a href="http://blog.51cto.com/2689556/843499">四、深入解析事件</a>
<a href="http://blog.51cto.com/2689556/843501">五、Lambda 表達式</a>
一、委托類型的來由
記得在使用C語言的年代,整個項目中都充滿着針指的身影,那時候流行使用函數指針來建立回調函數,使用回調可以把函數回調給程式中的另一個函數。但函數指針隻是簡單地把位址指向另一個函數,并不能傳遞其他額外資訊。
在.NET中,在大部分時間裡都沒有指針的身影,因為指針被封閉在内部函數當中。可是回調函數卻依然存在,它是以委托的方式來完成的。委托可以被視為一個更進階的指針,它不僅僅能把位址指向另一個函數,而且還能傳遞參數,傳回值等多個資訊。系統還為委托對象自動生成了同步、異步的調用方式,開發人員使用 BeginInvoke、EndInvoke 方法就可以抛開 Thread 而直接使用多線程調用 。
二、建立委托類
使用delegate就可以直接建立任何名稱的委托類型,當進行系統編譯時,系統就會自動生成此類型。您可以使用delegate void MyDelegate() 方式建立一個委托類,并使用ILDASM.exe觀察其成員。由ILDASM.exe 中可以看到,它繼承了System.MulticastDelegate類,并自動生成BeginInvoke、EndInvoke、Invoke 等三個常用方法。
<a href="http://blog.51cto.com/attachment/201204/203756883.jpg" target="_blank"></a>
Invoke 方法是用于同步調用委托對象的對應方法,而BeginInvoke、EndInvoke是用于以異步方式調用對應方法的。
MulticastDelegate是System.Delegate的子類,它是一個特殊類,編譯器和其他工具可以從此類派生,但是自定義類不能顯式地從此類進行派生。它支援多路廣播委托,并擁有一個帶有連結的委托清單,在調用多路廣播委托時,系統将按照調用清單中的委托出現順序來同步調用這些委托。
MulticastDelegate具有兩個常用屬性:Method、Target。其中Method 用于擷取委托所表示的方法Target 用于擷取目前調用的類執行個體。
MulticastDelegate有以下幾個常用方法:
方法名稱
說明
Clone
建立委托的淺表副本。
GetInvocationList
按照調用順序傳回此多路廣播委托的調用清單。
GetMethodImpl
傳回由目前的 MulticastDelegate 表示的靜态方法。
GetObjectData
用序列化該執行個體所需的所有資料填充 SerializationInfo 對象。
MemberwiseClone
建立目前 Object 的淺表副本。
RemoveImpl
調用清單中移除與指定委托相等的元素
MulticastDelegate與Delegate給委托對象建立了強大的支援,下面向各位詳細介紹一下委托的使用方式。
三、委托使用方式
3.1 簡單的委托
當建立委托對象時,委托的參數類型必須與委托方法相對應。隻要向建立委托對象的構造函數中輸入方法名稱example.Method,委托就會直接綁定此方法。使用myDelegate.Invoke(string message),就能顯式調用委托方法。但在實際的操作中,我們無須用到 Invoke 方法,而隻要直接使用myDelegate(string message),就能調用委托方法。
3.2 帶傳回值的委托
當建立委托對象時,委托的傳回值必須與委托方法相對應。使用下面的例子,方法将傳回 “Hello Leslie” 。
3.3 多路廣播委托
在第二節前曾經提過,委托類繼承于MulticastDelegate,這使委托對象支援多路廣播,即委托對象可以綁定多個方法。當輸入參數後,每個方法會按順序進行疊代處理,并傳回最後一個方法的計算結果。
下面的例子中,Price 類中有兩個計算方法,Ordinary 按普通的9.5折計算,Favourable 按優惠價 8.5 折計算。委托同時綁定了這兩個方法,在輸入參數100以後,Ordinary、Favourable這兩個方法将按順序疊代執行下去,最後傳回 Favourable 方法的計算結果 85。
運作結果
<a href="http://blog.51cto.com/attachment/201204/203821788.jpg" target="_blank"></a>
3.4 淺談Observer模式
回顧一下簡單的 Observer 模式,它使用一對多的方式,可以讓多個觀察者同時關注同一個事物,并作出不同的響應。
例如下面的例子,Manager的底薪為基本工資的1.5倍,Assistant的底薪為基本工資的1.2倍。WageManager類的RegisterWorker方法與RemoveWorker方法可以用于注冊和登出觀察者,最後執行Execute方法可以對多個已注冊的觀察者同時輸入參數。
<a target="_blank" href="http://blog.51cto.com/attachment/201204/204100620.jpg"></a>
開發 Observer 模式時借助委托,可以進一步簡化開發的過程。由于委托對象支援多路廣播,是以可以把Worker類省略。在WageManager類中建立了一個委托對象wageHandler,通過Attach與Detach方法可以分别加入或取消委托。如果觀察者想對事物進行監測,隻需要加入一個委托對象即可。記得在第二節曾經提過,委托的GetInvodationList方法能擷取多路廣播委托清單,在Execute方法中,就是通過去多路廣播委托清單去判斷所綁定的委托數量是否為0。
最後運作結果與上面的例子相同。
3.5 委托的協變與逆變
在 Framework 2.0 出現之前,委托協變這個概念還沒有出現。此時因為委托是安全類型,它們不遵守繼承的基礎規則。即會這下面的情況:Manager 雖然是 Worker 的子類,但 GetWorkerHander 委托不能直接綁定 GetManager 方法,因為在委托當中它們的傳回值 Manager 與 Worker 被視為完全無關的兩個類型。
自從Framework 2.0 面試以後,委托協變的概念就應運而生,此時委托可以按照傳統的繼承規則進行轉換。即 GetWorkerHandler 委托可以直接綁定 GetManager 方法。
委托逆變,是指委托方法的參數同樣可以接收 “繼承” 這個傳統規則。像下面的例子,以 object 為參數的委托,可以接受任何 object 子類的對象作為參數。最後可以在處理方法中使用 is 對輸入資料的類型進行判斷,分别處理對不同的類型的對象。
<a target="_blank" href="http://blog.51cto.com/attachment/201204/204121691.jpg"></a>
注意:委托與其綁定方法的參數必須一至,即當 Handler 所輸入的參數為 object 類型,其綁定方法 GetMessage 的參數也必須為 object 。否則,即使綁定方法的參數為 object 的子類,系統也無法辨認。
3.6 泛型委托
委托逆變雖然實用,但如果都以 object 作為參數,則需要每次都對參數進行類型的判斷,這不禁令人感到厭煩。
為此,泛型委托應運而生,泛型委托有着委托逆變的優點,同時利用泛型的特性,可以使一個委托綁定多個不同類型參數的方法,而且在方法中不需要使用 is 進行類型判斷,進而簡化了代碼。
<a target="_blank" href="http://blog.51cto.com/attachment/201204/204138937.jpg"></a>
<a href="http://79100812.blog.51cto.com/2689556/843489#a0">回到目錄</a>
本文轉自 leslies2 51CTO部落格,原文連結:http://blog.51cto.com/79100812/843489