程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> Delegate背後的秘密,Delegate秘密

Delegate背後的秘密,Delegate秘密

編輯:C#入門知識

Delegate背後的秘密,Delegate秘密


表面上看來使用delegate是一件很簡單的事。

用delegate關鍵字定義,使用老套的new創建一個instance ,使用熟悉的方法調用寫法調用,只不過不在是方法名,而是委托名。 但是在這背後CLR為我們做了很多。   當我們 寫下下面這句話時   public delegate void FeedBack(Int32 val);   其實相當於 大概下面的代碼(省略部分多線程相關代碼)   pubic class FeedBack:MulticastDelegate {   public FeedBack(Object  object ,  IntPtr  methodPtr)   {    xxxx
  }   public virtual void Invoke(int32 val); }   首先delegate 就是一個class。這個class會自動繼承MuticastDelegate .然後,這個class 的構造函數就如上面所示,  我們先看第二個參數 methodPtr  可以理解為函數的指針,或者說函數的地址。委托在真正運行的時候,就要找到這個函數的地址,並運行這個函數。對,我們要先找到這個函數的地址,對於 static類型的函數,地址是確定的,而對於實例函數而言,內部的函數地址則是不定的,它需要先確定具體的某個實例, 這既是第一個參數的用途,具體實例的引用,對於static類型的函數,就會默認傳入一個null。 其實,在基類 MutiCastDelegate中,有三個重要的非公開屬性   _target  _methodPtr _invokationlist   一二兩個屬性 剛好對應我們構造函數傳入的兩個參數,第三個屬性和委托鏈有關,默認為null,我們先不討論。   也就是說:delegate 其實只是個wrapper ,包裝著需要真正操作的對象 以及它的方法。   也就是說委托的構造函數 完成了 和具體真實對象以及真實方法的對應,而調用則是另一個方法 Invoke(int32 val)。這個方法會觸發真實方法的調用。  
關於委托鏈式。。。我就不深究了,因為實在比較懶。。 可以理解為 一個delegate數組一次存放著這些委托,然後調用的時候,內部通過foreach一個一個調用。。.也就造成了整個委托鏈中的方法都被調用的 “鏈式反應”。因為內部實現也就是數組,所以 具體調用次序是確定的,而不是隨機的,根據的加入委托鏈中的順序會依次調用這些方法。 如何拼成一個委托鏈,有兩種寫法   在早期版本中 通過 Delegate.Combine 靜態方法來拼接鏈,(每次拼接要記得賦給原先的鏈)。就像現實生活中裝鏈條一樣,有兩個參數,第一個參數是 已裝的部分鏈,第二個參數是待裝的。(鏈子的內部是依靠數組實現的)   第二種寫法,是C#為了更好的輔助delegate而 自動對+= 和-=這兩個操作符提供了重載。也就是說delegate實例之間可以簡單的通過 +=和-=來拼接。這無疑相當大的簡化了 委托鏈。(至於怎麼重載的,因為懶。。就不模擬了。)   拼完委托鏈之後,只需要直接調用委托一樣調用委托鏈即可,委托內部的算法會依次調用每個拼接的元素。但有一個缺憾就是,如果委托是帶有返回值的,那麼只能返回最後一個委托執行的返回值前面所有委托的返回值都會被丟棄。 有什麼辦法呢,如下。

委托鏈的高級調用   如上所說,如果直接invoke委托鏈的話,那麼只有鏈尾的返回值會輸出出來,並且簡單直接的鏈式調用還有巨大的隱患,如果其中的某個delegate拋出異常,那整個鏈都會卡住   。如果想要的每一個委托的返回值,就不能用上面的方法。  
委托與泛型   我們先定義一個委托   public void FeedBack(Int32 value);   然後我們又突然需要另一個委托。。o(╯□╰)o   public void FeedBack(string value)   可以這樣來定義嗎? ,絕對不可以!!委托的本質是class 所以你上面的做法只是重復定義了同名的類罷了。   委托可以融入泛型來更加強大   ,看下面的完整實例      
 public delegate void FeedBack <T>(T para);
    public delegate void FeedBack<Tint,Tstr>(Tint para1,Tstr para2);
    class 委托可以有泛型嗎
    {
        public static void Main(string[] args)
        {           
            FeedBack<Int32 > fb = new FeedBack<int >(FeedBackInt);
            FeedBack<String > fb2 = new FeedBack<string >(FeedbackString);
            FeedBack<int ,string > fb3 = new FeedBack<int , string >(FeedbackIntString);
            fb.Invoke(2);
            fb2.Invoke( "haha");
            fb3.Invoke(60,"haha" );
            Console.ReadKey();
        }
 
        public static void FeedBackInt(Int32 val)
        {
            Console.WriteLine(val);
        }
        public static void FeedbackString(String str)
        {
            Console.WriteLine(str);
        }
        public static void FeedbackIntString(int val,string str)
        {
            Console.WriteLine(val+":" +str);
        }
    }
}

 

  不知道看到這裡,你心裡會不會隱隱約約有一種想法。。。。那就是微軟所想到的:通過泛型可以把你編程幾乎會用到的所有可能的委托囊括!!!! 他們確實這樣做了。。這就是FCL 擁有的自建委托。不帶返回值的統稱  Action  帶返回值的統稱  Func. 自帶委托如下,參數最多 上限16個。再多的話,這個方法本身就很有問題了。     以及   微軟建議,在需要使用委托的時候就是用內建的這些委托,而不要再去自己創建委托類型了。那我們就遵守這個慣例吧。 當然 也有可能這些泛型不符合你需要的委托。 比如你需要這個 delegate void bar(ref  int32 z);  ,或者說你的泛型委托需要一些約束。那就得自己定義委托了。。    
最常見的delegate的用途應該是webform中的事件處理機制。 我們常常會見到這樣的寫法: button1.Click += button1_Click;   void button1_Click(Object sender, EventArgs e) {
// Do something, the button was clicked...
}   在學習的時候竟然很少有人對這種寫法感到詫異。。 這是C# 提供的語法糖。 正常的寫法是 button1.Click += new EventHandler(button1_Click); 現在認識麼? 就是簡單的拼接 delegate 鏈而已╮(╯_╰)╭。  上面的語法糖是委托裡的第一種語法糖。 在需要委托實例的地方 不再需要委托實例,直接提供委托要包含的的函數名即可。   (events 是更安全的delegate .實際上是使用自建的EventHandler委托,然後,每次觸發不同控件的時候,會通過EventHandler(sender,e)) 這個委托來觸發實際的方法。   ok 委托先到這裡。    

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved