委託的定義和使用入門

6.3.1 C#中定義委託

       C#中使用一個類時,分兩個階段。首先需要定義這個類,即告訴編譯器這個類由什麼字段和方法組成。然後(除非只使用靜態方法)實例化類的一個對象。使用委託時,也需要經過這兩個步驟。首先定義要使用的委託,對於委託,定義它就是告訴編譯器這種類型的委託代表了哪種類型的方法,然後創建該委託的一個或多個實例。編譯器在後臺將創建表示該委託的一個類。

定義委託的語法如下:

訪問修飾符 delegate 返回值類型 委託名稱(參數1, 參數1, 參數n);

    //------------例如--------------------

    //不返回值,也沒有參數的委託類型BB

    public delegate void BB(); 

   //返回string類型值,需要兩個參數的委託類型AA

public delegate string AA(int a, string b);

委託的一個要點是它們的類型安全性非常高。在定義委託時,必須給出它所代表的方法簽名和返回類型等全部細節。

       如果覺得不好理解可以把委託當作給方法簽名和返回類型指定名稱。因爲其語法類似於方法的定義,但沒有方法體,定義的前面要加上關鍵字delegate。因爲定義委託基本上是定義一個新類,所以可以在定義類的任何地方定義委託,既可以在另一個類的內部定義,也可以在任何類的外部定義,還可以在命名空間中把委託定義爲頂層對象。根據定義的可見性,可以在委託定義上添加一般的訪問修飾符:publicprivateprotected等:

實際上,“定義一個委託”是指“定義一個新類”。委託實現爲派生自基類System. Multicast Delegate的類,System.MulticastDelegate又派生自基類System.DelegateC#編譯器知道這個類,會使用其委託語法,因此我們不需要了解這個類的具體執行情況,這是C#與基類共同合作,使編程更易完成的另一個示例。

      定義好委託後,就可以創建它的一個實例,以存儲特定方法的細節。

6.3.2 使用委託

       委託是一種安全地封裝方法的類型,它與 C  C++ 中的函數指針類似。與 C 中的函數指針不同,委託是面向對象的、類型安全的和保險的。委託的類型由委託的名稱定義。下面的示例聲明瞭一個名爲 Del 的委託:

    //聲明瞭一個名爲 Del 的委託,該委託可以封裝一個採用字符串作爲參數並返回 void 的方法。

    public delegate void Del(string message);

構造委託對象時,通常提供委託將包裝的方法的名稱或使用匿名方法。實例化委託後,委託將把對它進行的方法調用傳遞給方法。調用方傳遞給委託的參數被傳遞給方法,來自方法的返回值(如果有)由委託返回給調用方。這被稱爲調用委託。可以將一個實例化的委託視爲被包裝的方法本身來調用該委託。例如:

using System;

 

namespace _63demo

{

    //聲明瞭一個名爲 Del 的委託,該委託可以封裝一個採用字符串作爲參數並返回 void 的方法。

    public delegate void Del(string message);

 

    class Program

    {

        //新增一個方法,

        public static void DelegateMethod(string message)

        {

            System.Console.WriteLine(message);

        } 

 

        static void Main()

        {

            //爲新申明的委託實例的委託對象爲DelegateMethod,這句有點繞,這樣理解

            //既然是委託,那麼肯定會有個最終執行者,反正不要期望委託本身幹事

            Del handler = DelegateMethod;

            //使用委託,其實等同於直接使用DelegateMethod方法,因爲最終是委託給這個方法的

            handler("調用委託來了");

            Console.ReadLine();

        }

    }

}

小天:沒覺得有什麼意思,要調用方法DelegateMethod,我直接調用就行了,好像犯不着繞一大圈。你看我要顯示一個消息,我首先去找到一箇中介所(委託),然後將要顯示的消息交給中介所,然後他來幫我顯示。

老田:現在社會上形形色色的中介機構那麼多,要知道存在即是合理。就上面實例來說,顯示一個消息當然沒有必要找中介。但如果你要做的是很複雜的事呢?下面我們再來將委託進行二次外包。有點像你喜歡上某個美女,但是你不好意思跟她說。於是你找了個做媒的人,然後你將你要給這個女孩子的定情信物和媒人一起給了一個專業說媒的方法(可以理解這個方法就是一個說媒的場景)。還是繼續使用上面已經有的代碼,只是新增一個方法和相應的調用語句,具體執行代碼如下:

        /// <summary>

        /// //新增一個方法,就是說媒的場景

        /// </summary>

        /// <param name="param1">參數(男孩信息)1</param>

        /// <param name="param2">參數(所給信物)2</param>

        /// <param name="callback">媒婆</param>

        public static void MethodWithCallback(string param1, string param2, Del callback)

        {

            //多個字符串相加最好用這種方式,性能更高

            StringBuilder say = new StringBuilder();   

            say.Append("有個臭P的傢伙喜歡你,他的信息如下:/n");

            say.Append(param1);

            say.Append("/n他還送上");

            say.Append(param2);

            say.Append("作爲定情信物");

 

            callback(say.ToString());

        }

 

        static void Main()

        {

            Del handler = DelegateMethod;

            string info = "名叫小天,年方二八,除了沒錢和長得太帥外基本沒有缺點"; //個人信息

            string gift = "天轟穿趣味編程系列圖書";                               //信物

            MethodWithCallback(info, gift, handler);         //將委託也作爲參數傳遞

 

            Console.ReadLine();

        }

       執行後效果如圖6-4

 

                                   6-4

小天:爲什麼上面的方法都是靜態(static)的呢?

老田:因爲這是個控制檯應用程序,而控制檯應用程序的入口方法Mainstatic的。要使這些方法能夠被Main調用,當然只能是靜態,這個都不清楚的話趕緊回去複習類設計那一章。

另外,將委託構造爲包裝實例方法時,該委託將同時引用實例和方法。除了它所包裝的方法外,委託不瞭解實例類型,所以只要任意類型的對象中具有與委託簽名相匹配的方法,委託就可以引用該對象。將委託構造爲包裝靜態方法時,它只引用方法。比如下面示例,我們新增一個類,裏面增加兩個方法,然後分別交給委託:

    //新增的類,裏面有三個方法

    public class MethodClass

    {

        public void Method1(string msg)

        {

            Console.WriteLine("第一個方法的結果是:"+msg);

        }

        public void Method2(string msg)

        {

            Console.WriteLine("第二個方法的結果是:"+msg);

        }

 }

        //在Main函數中使用

        static void Main()

        {

            MethodClass mc = new MethodClass(); //實例化一個被調用類的實例

            //注意下面三個關聯的方法,他們的類實例是不同的,但是它們的方法簽名是一樣的

            Del handler1 = mc.Method1;

            Del handler2 = mc.Method2;

            //下面這個方法是前面示例使用的方法

            Del handler = DelegateMethod; 

            //調用委託

            handler1("小天");

            handler2("老田");

 

            Console.ReadLine();

        }

       小天:也就是說,委託只管被委託的方法的返回類型和參數列表是符合的就可以了,並不太在意具體的類實例。難道這就是傳說的“英雄不問出生”?

       老田:另外,我們還可以將上面三個委託來個批發,現在將上例中三個委託實例handler1handler2handler3來個打包執行,代碼如下:

        //在Main函數中使用

        static void Main()

        {

            MethodClass mc = new MethodClass(); //實例化一個被調用類的實例

           Del handler1 = mc.Method1;

            Del handler2 = mc.Method2; 

            Del handler3 = DelegateMethod;

 

            Del handler4 = handler1 + handler2; //第一種打包方法

            handler4 += handler3;               //將handler3添加到handler4的執行序列中

            //調用委託

            handler4("打包執行");

        }

6.3.3 多播委託

此時,handler4在其調用列表中包含三個方法 -- Method1Method2  DelegateMethod。原來的三個委託 handler1handler2 handler3 保持不變。調用 allMethodsDelegate 時,將按順序調用所有這三個方法。如果委託使用引用參數,則引用將依次傳遞給三個方法中的每個方法,由一個方法引起的更改對下一個方法是可見的。如果任一方法引發了異常,而在該方法內未捕獲該異常,則該異常將傳遞給委託的調用方,並且不再對調用列表中後面的方法進行調用。如果委託具有返回值和/或輸出參數,它將返回最後調用的方法的返回值和參數。若要從調用列表中移除方法,可以使用減法運算符或減法賦值運算符(“-”或“-=”)。例如:

            // 從handler4中移除handler3

            handler4 -= handler3;         

            //新申明一個handler5,等於handler4移除handler2後的結果

            Del handler5 = handler4 - handler2;

       小天:這個就是前面實例中講到的多播委託吧。這個我知道了,不過我發現這麼加加減減的,搞到最後,有什麼辦法知道到底委託序列中有多少個方法不?

       老田:由於委託類型派生自 System.Delegate,所以可在委託上調用該類定義的方法和屬性。例如,爲了找出委託的調用列表中的方法數,您可以編寫下面的代碼:

            //獲取多路廣播(簡稱“多播”)委託中的方法數量

            handler5.GetInvocationList().GetLength(0); 

       多路廣播委託廣泛用於事件處理中。事件源對象向已註冊接收該事件的接收方對象發送事件通知。爲了爲事件註冊,接收方創建了旨在處理事件的方法,然後爲該方法創建委託並將該委託傳遞給事件源。事件發生時,源將調用委託。然後,委託調用接收方的事件處理方法並傳送事件數據。給定事件的委託類型由事件源定義。

       小天:能夠對多播委託實例進行遍歷不?

       老田:可以的,如下例:

        static void Main()

        {

            MethodClass mc = new MethodClass();     //被調用方法的類實例

            Del d1, d2, d3, d4;                     //申明多個委託實例

            d1 = mc.Method1;                       

            d2 = mc.Method2;

            d3 = DelegateMethod;

            d4 = d1 + d2 + d3;                      //d4等於前面三個相加

 

            //申明一個委託類型的數組,將d4中的調用列表作爲值給它

            Delegate[] delegates = d4.GetInvocationList();

            //申明一個變量i作爲計數器

            int i = 0;

            foreach (Del d in delegates)

            {

                i++;        //i遞加

                d("我是方法 "+i.ToString());    //調用當前委託

            }

        }

執行後效果如圖6-5

 

                                                 6-5

       對於多播委託的更多操作我就不一一介紹了,你自己結合動態幫助去嘗試吧。


注意,多路廣播委託聲明時必須返回void,否則返回值不知道應該送回什麼地方。
對此,我做了一個測試:如果不將委託的聲明返回void,則返回值返回的是最後一個鏈入委託鏈的方法的返回值,編譯不會出錯。

爲什麼要用委託
   使用委託使程序員可以將方法引用封裝在委託對象內。然後可以將該委託對象傳遞給可調用所引用方法的代碼,而不必在編譯時知道將調用哪個方法。
與C或C++中的函數指針不同,委託是面向對象,而且是類型安全的。



轉自:http://blog.csdn.net/thcjp/article/details/5508138  天轟穿


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章