深入理解C#委託及原理

一、委託
設想,如果我們寫了一個廚師做菜方法用來做菜,裏面有 拿菜、切菜、配菜、炒菜 四個環節,但編寫此方法代碼的人想讓 配菜 這個環節讓調用方法的人實現,換句話說,就是想在方法被調用時接收 代碼 作爲參數,在方法中執行這端傳進來的代碼。
但,怎麼爲一個方法傳 代碼 進來呢?當然大家想到了傳遞接口方式來實現,咱先不討論接口,因爲微軟爲我們提供了一個叫做 【委託】 的類型。
 
(一)、委託基礎:
1.先看看代碼:
(1).定一個方法:void SayHi(string name){Console.WriteLine(“Hi~”+name+”! ” );}
(2).聲明一種委託類型:delegate void DGSayHi(string uName);
(3).創建委託類型對象:DGSayHi dgObj = new DGSayHi(SayHi);//構造函數中傳入了方法
(4).執行委託:
dgObj(“JamesZou”); //調用委託(奇怪:對象加括號 的方式調用?後面解釋。)
輸出:Hi~JamesZou!
 
2.什麼是委託?
(1)概念:“C# 中的委託類似於 C 或 C++ 中的函數指針。使用委託使程序員可以將方法引用封裝在委託對象內。然後調用該委託對象就可以執行委託對象內方法引用指向的方法,而不必在編譯時知道將調用哪個方法(如參數爲委託類型的方法,也就是提供了爲程序回調指定方法的機制)。”-- 引自MSDN

(2)通俗:就是一個能存放很多方法的指針的調用清單(但方法簽名必須和委託類型簽名一樣),你一調用這個清單,那麼清單裏的所有的指針所對應的方法就會依次被執行。

(3)比方說:有三臺機器A、C、D,點一個紅色按鈕就會運行。操作人員接到指令,要求在接到電話後分別打開AD機器,然後然後工人就在接到電話後,先後打開AD機器。(此例中的 三臺機器就是方法,操作員,就可以看成是“委託”啦)
 
(4)概要圖例:
DGSayHi dgObj = new DGSayHi(SayHi);
dgObj(“James”); //調用委託對象,就會執行委託對象裏的方法。
3.委託有什麼用?
A.能夠幫程序員在需要時,根據條件動態執行多個方法:(接上例代碼)
(1)定三個方法:
void SayHi(string name){Console.WriteLine(“Hi~”+name ); }
void DaZhaoHu(string name){ Console.WriteLine(“你好啊~”+name ); }
string OHaUo(string name){ Console.WriteLine(“OHaUo ~”+name ); return “JapHi”;}

(2)創建委託類型對象,並通過構造函數傳參方式向委託對象“註冊”第一個方法:
DGSayHi dgObj = new DGSayHi(SayHi);

(3)繼續“註冊兩個方法”:
dgObj+=DaZhaoHu;// (奇怪:對象之間用+=符號來操作?後面解釋)
//dgObj+=OhaUo;//註釋此行代碼,因爲編譯時報錯,OhaUo方法簽名與委託類型的簽名不一致(委託簽名無返回值)。
 
(4)執行委託對象:
dgObj(“James”); //執行了此委託中註冊的兩個方法
輸出:
Hi~James
你好啊~James
      
(5)概要圖例
B. 委託作爲方法參數(回調方法機制)
(1).接上例代碼,再定義一個方法:
void DoTestDelegateFun(DGSayHi dgObj){dgObj(“鋼鐵俠”);}
(2).調用此方法:
DoTestDelegateFun(SayHi);//輸出:Hi~鋼鐵俠(奇怪:竟然直接傳方法了?後面解釋)
 
      C.委託語法糖
        (1).注意到上面有3個地方我們都覺得“奇怪”:
        a.調用委託對象dgObj(“JamesZou”);
        b.向委託註冊方法 dgObj+=DaZhaoHu;
        c.將方法作爲參數 DoTestDelegateFun(SayHi);
        這些用法其實都是FW爲我們提供的簡便語法(它們有個可愛的名字:語法糖),在編譯時由編譯器轉成完整的代碼:
        a. dgObj.Invoke(“JamesZou”);
        b. dgObj = (DGSayHi) Delegate.Combine(dgObj, new DGSayHi(this.DaZhaoHu));//Combine方法將第二個參數,添加到dgObj中,並返回委託對象。
        c. this.DoTestDelegateFun(new DGSayHi(this.SayHi));
        Delegate類、Invoke方法、Combine方法是哪來的呢?
(二)、委託原理
1.delegate 關鍵字
(1).概念:delegate 關鍵字用於聲明一個引用類型,該引用類型可用於封裝命名方法或匿名方法。
(2).編譯後生成的的中間代碼。 
請大家思考一下,關鍵字是類型嗎?不是。那編譯器遇到這個關鍵字做了什麼事情?藉助【IL反彙編程序】 我們來看一看:
a.開始-程序-如圖:
b.打開項目文件夾下的binDebug文件夾,找到程序集 CodeForFun.exe,拖入到【IL反彙編程序】界面中便可看到程序集的IL代碼:
找到我們定義了委託DGSayHi的類DelegateForFun,發現,裏面的 委託類型聲明 代碼
編譯前:delegate string DGSayHi(string uName);
變成了一個類:
單擊展開後我們再來看看:
看出什麼了?
(I).繼承了System.MulticastDelegate。
(II).包含了構造方法、BeginInvoke、EndInvoke、Invoke方法。
也就是說此時,delegate代碼已經編譯成了如下代碼:
編譯後:
class DGSayHi:System.MulticastDelegate 
{
  public DelegateForFun();
     void Invoke(string value);
     IAsyncResult BeginInvoke(string value,AsyncCallback callback,Object object);
     void EndInvoke(IAsyncResult result);
}
(3)System.MulticastDelegate 類
下面我們來看看藉助.Net Reflector工具來查看類庫中的 MulticastDelegate 類
public abstract class MulticastDelegate : Delegate
由此我們可以看出繼承關係:DGSayHi –> MulticastDelegate –> Delegate
MulticastDelegate類中有3個重要的成員,其中兩個繼承自 Delegate  :
a.三者的作用:
_methodPtr 裏保存的就是 方法指針。
_target 裏用來保存方法所在的對象。
_invocationList 其實使用時是個object數組,在註冊多個方法時,其他方法就保存在此成員中,而它也就是 委託鏈 的關鍵容器。
 
b.概要圖:
圖中的委託對象 dgObj 在創建時創建了指向方法 SayHi的指針並保存在 _methodPtr中;_target中保存了SayHi方法所在的類的對象(比如我把這段代碼寫在窗體裏按鈕的點擊方法中,那麼此時 _target就是 SayHi方法所在的窗體對象);_invocationList 中保存了追加的兩個方法的指針,但這兩個方法指針都是分別被裝在 MuticastDelegate對象中。
發佈了25 篇原創文章 · 獲贊 6 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章