委託的初步理解和用法

委託是用來處理其他語言需要用函數指針來處理的情況的。

都說C#沒有指針,而委託就相當於是個函數指針,不過也有很多不同點,委託是完全面向對象的。

C++中指針僅指向成員函數,委託同時封裝了對象實例和方法。

 

C#的叫法

函數:方法

返回值+參數列表:簽名

 

把它和函數指針放一起說可能會比較好理解委託的作用。

首先,委託是個類,委託聲明定義一個System.Delegate類派生的類

委託是可以保存對方法的引用的類。與其他的類不同,委託類具有一個簽名,並且它只能對與其簽名匹配的方法進行引用。這樣,委託就等效於一個類型安全函數指針。

委託實例(也就是類的對象)封裝了一個調用列表,該列表列出了一個或多個方法,每個方法稱爲一個可調用實體。

 

可以說委託對象可以指向很多個其他類的方法,或者說委託這個時候相當於一個函數指針,可以指向其他類的一個或多個成員函數。

 

當然委託對象所指向的方法的簽名,一定要是跟聲明委託時的簽名一樣。也就是返回值和參數列表要一樣。

 

Ø  委託的聲明

[委託修飾符] delegate 返回值類型委託名([形參列表]);

藍色部分就是該委託的簽名

委託聲明的語法與一般C#類的聲明語法不一致。C#編譯器會根據委託的聲明語法,自動創建一個派生於System.MulticastDelegat的類及其相關實現細節。

                  簡單說,上面的聲明在編譯的時候,實際上會轉換成一個類,類的成員函數自動生成。

 

Ø  委託的實例化和調用

聲明瞭委託(實際上是個派生類),需要創建委託的實例(對象),然後調用其方法(函數成員)

創建委託實例的基本形式如下:

委託名委託實例名 = new 委託名(匹配方法);

委託名委託實例名 = 匹配方法 ;   //等價簡寫

 

委託實例名的同步調用與方法的調用:

委託實例名.Invoke(實參列表);

委託實例名(實參列表);

 

 

簡單看個例子理解委託的實例化和調用        

using System;

namespaceCSharpBook.Chapter09

{

    delegatevoidD(int x);       // 聲明委託

    classC

    {

        publicstaticvoid M1(int i){Console.WriteLine("C.M1:" + i);}

         publicstaticvoid M2(int i){Console.WriteLine("C.M2:" + i);}

        publicvoid M3(int i){Console.WriteLine("C.M3:" + i);}

    }

    classTest

    {

        staticvoid Main()

        {

            D d1 = newD(C.M1);  //使用new關鍵字,創建委託對象,指向類靜態方法

            d1(-1);              //調用M1

            D d2 = C.M2;        //使用賦值運算符,創建委託對象,指向類靜態方法

            d2(-2);             //調用M2

            C objc = newC();

            D d3 = newD(objc.M3); //使用new關鍵字,創建委託對象,指向對象實例方法

            d2(-3);              //調用M3

            Console.ReadKey();

        }

    }

}

 

再看個稍微複雜點的

using System;

namespaceCSharpBook.Chapter09

{

    delegatevoidD(int[] A);   // 聲明委託

    classArraySort

{  

        publicstaticvoid DisplayArray(int[] A) //打印數組

        foreach (int i in A) Console.Write("{0,5} ", i); Console.WriteLine(); }

        publicstaticvoid GeneralSort(int[] A, D sort)

        {  //通用排序程序

            sort(A);  // 調用排序算法,委託實例名(實參列表);

            Console.WriteLine("升序數組: "); DisplayArray(A); //顯示數組

         }

        publicstaticvoid BubbleSort(int[] A)

        {  //冒泡算法

            int i, t;

            int N = A.Length;   //獲取數組A的長度N

            for (int loop = 1; loop <=N - 1; loop++)//外循環進行N-1輪比較

            {  for (i = 0; i <= N -1 - loop; i++) //內循環兩兩比較,大數下沉

                  if (A[i] > A[i +1])       //相鄰兩數交換

                  { t = A[i]; A[i] = A[i + 1];A[i + 1] = t; }

            }

        }

        publicstaticvoid SelectSort(int[] A)

        {  //選擇算法

            int i, t, MinI;

            int N = A.Length;  //獲取數組A的長度N

            for (int loop = 0; loop<= N - 2; loop++)//外循環進行N-1輪比較

            { MinI = loop;

               for (i = loop; i <=N - 1; i++) //內循環中在無序數中找最小值

                   if (A[i] < A[MinI])MinI = i;

               t = A[loop]; A[loop] = A[MinI];A[MinI] = t;//最小值與第一個元素交換

            }

        }

        staticvoid Main()

        {  int[] A = newint[10];  Random rNum = newRandom();

            //數組A賦值(0~100之間的隨機數)

            for (int i = 0; i <A.Length; i++) A[i] = rNum.Next(101);

            Console.WriteLine("原始數組: "); DisplayArray(A); //顯示數組

            D d1 = newD(ArraySort.BubbleSort);//創建委託實例,指向冒泡算法

            Console.Write("冒泡算法---"); GeneralSort(A, d1);

            D d2 = newD(ArraySort.SelectSort); //創建委託實例,指向選擇算法

            Console.Write("選擇算法---"); GeneralSort(A, d2); Console.ReadKey();

        }

    }

}

 

Ø  匿名委託

匿名方法。即無須先聲明類或結構體以及與委託匹配的方法,而是在創建委託的實例時,直接聲明與委託匹配的方法的代碼塊。

簡單說,就是把委託實例所指向的方法直接寫在這個委託下面,所以不需要單獨聲明這個所指向的方法。

聲明匿名委託的基本語法爲:

委託名委託實例名 = new delegate [形參列表]

{

         方法體;

}

 

例如

staticvoid Main()

   // 使用匿名方法實例化delegate

         Printer p = delegate(string j)

         {

Console.WriteLine(j);

    };

         p("使用匿名方法的委託的調用。"); //匿名delegate調用結果

}

 

Ø  多播委託

個人認爲多播委託也是跟函數指針有所區別的一個地方

函數指針指向的是一個內存單元,這個單元指向一個函數。而多播委託就是可以讓一個委託對象指向多個函數。

多播委託包括Combine RemoveRemoveAll三個靜態方法,用於添加、刪除指向的函數到調用列表。也可以用+或+=  -或-= 進行添加、刪除

委託類 D

         創建委託實例d1 d2 d3 d4...假設每個委託實例指向的函數不一樣,但簽名一定要一樣。

d1+=d2;

那麼調用d1的時候就會依次調用d1d2。每個指向的函數函數的形參列表也都一樣。

<委託類型> <實例化名>+=new <委託類型>(<註冊函數>)
例子:CheckDelegate _checkDelegate=new CheckDelegate(CheckMod);//將函數CheckMod註冊到委託實例_checkDelegate
也可以直接將匹配的函數註冊到實例化委託:
<委託類型> <實例化名>+=<註冊函數>
例子:CheckDelegate _checkDelegate+=CheckMod;//將函數CheckMod註冊到委託實例_checkDelegate

另外有一點需要注意的是,如果對註冊了函數的委託實例從新使用=號賦值,相當於是重新實例化了委託,之前在上面註冊的函數和委託實例之間也不再產生任何關係

 

Ø  委託的異步調用

//沒怎麼看懂,先跳過。。。

 

 

 

Ø  委託的兼容性

如果硬性要求委託的簽名和所匹配的方法的簽名完全一樣(返回值類型一樣,參數列表一樣),用起來就會很侷限。

也就是說,委託的簽名和所匹配的方法的簽名也可以不完全一樣,簡單來說需要滿足以下條件:(假設委託類型D,方法M)

1.      D 和M的參數數目相同,且各自對應參數具有相同的ref或out修飾符;

2.      對於每個ref或out參數,D中的參數類型與M中的參數類型相同;

3.      存在從M的返回類型到D的返回類型的隱式引用轉換(協變);

4.      每一個值參數(非refout修飾符的參數)都存在從D中的參數類型到M中的對應參數類型的隱式引用轉換(逆變)

 

舉個例子,

現在有People類,派生出Student類

方法 : Student M(string name);

委託簽名 :delegate People D(string name);

Student可以隱式轉換成People

 

方法:void M(People p);

委託簽名:delegate void D(Student s

D的委託實例一樣可以指向方法M,兼容

 

所謂隱式轉換,就是系統默認的轉換,其本質是小存儲容量數據類型自動轉換爲大存儲容量數據類型。 

有如下幾種: 

從sbyte類型到short,int,long,float,double,或decimal類型。 

從byte類型到short,ushort,int,uint,long,ulong,float,double,或decimal類型。

從short類型到int,long,float,double,或decimal類型。 

從ushort類型到int,uint,long,ulong,float,double,或decimal類型。

從int類型到long,float,double,或decimal類型。 從uint類型到long,ulong,float,double,或decimal類型。 

從long類型到float,double,或decimal類型。 從ulong類型到float,double,或decimal類型。 

從char類型到ushort,int,uint,long,ulong,float,double,或decimal類型。 從float類型到double類型。

 

 

//以上只是委託的基本概念和用法,與事件結合後使用會比較靈活。

//還有泛型委託和泛型事件。。。

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