ch01.深入理解C#委託及原理_《沒有控件的ASPDONET》

一、委託
設想,如果我們寫了一個廚師做菜方法用來做菜,裏面有 拿菜、切菜、配菜、炒菜 四個環節,但編寫此方法代碼的人想讓 配菜 這個環節讓調用方法的人實現,換句話說,就是想在方法被調用時接收代碼 作爲參數,在方法中執行這端傳進來的代碼。
但,怎麼爲一個方法傳 代碼 進來呢?當然大家想到了傳遞接口方式來實現,咱先不討論接口,因爲微軟爲我們提供了一個叫做 【委託】 的類型。

 

(一)、委託基礎:
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)
比方說:有三臺機器ACD,點一個紅色按鈕就會運行。操作人員接到指令,要求在接到電話後分別打開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 = (DGSayHiDelegate.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.
打開項目文件夾下的bin\Debug文件夾,找到程序集 CodeForFun.exe,拖入到【IL反彙編程序】界面中便可看到程序集的IL代碼:
找到我們定義了委託DGSayHi的類DelegateForFun,發現,裏面的 委託類型聲明 代碼
編譯前:delegate string DGSayHi(string uName);
變成了一個類:
   

單擊展開後我們再來看看:
   看出什麼了?
(I).
繼承了System.MulticastDelegate
(II).
包含了構造方法、BeginInvokeEndInvokeInvoke方法。
也就是說此時,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對象中。
轉載請註明出處:ch01.深入理解C#委託及原理 
開智網http://www.oumind.com
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章