委託的初步理解和用法
委託是用來處理其他語言需要用函式指標來處理的情況的。
都說C#沒有指標,而委託就相當於是個函式指標,不過也有很多不同點,委託是完全面向物件的。
C++中指標僅指向成員函式,委託同時封裝了物件例項和方法。
C#的叫法
函式:方法
返回值+引數列表:簽名
把它和函式指標放一起說可能會比較好理解委託的作用。
首先,委託是個類,委託宣告定義一個從System.Delegate類派生的類。
委託是可以儲存對方法的引用的類。與其他的類不同,委託類具有一個簽名,並且它只能對與其簽名匹配的方法進行引用。這樣,委託就等效於一個型別安全函式指標。
委託例項(也就是類的物件)封裝了一個呼叫列表,該列表列出了一個或多個方法,每個方法稱為一個可呼叫實體。
可以說委託物件可以指向很多個其他類的方法,或者說委託這個時候相當於一個函式指標,可以指向其他類的一個或多個成員函式。
當然委託物件所指向的方法的簽名,一定要是跟宣告委託時的簽名一樣。也就是返回值和引數列表要一樣。
Ø 委託的宣告
[委託修飾符] delegate 返回值型別委託名([形參列表]);
藍色部分就是該委託的簽名
委託宣告的語法與一般C#類的宣告語法不一致。C#編譯器會根據委託的宣告語法,自動建立一個派生於System.MulticastDelegat的類及其相關實現細節。
簡單說,上面的宣告在編譯的時候,實際上會轉換成一個類,類的成員函式自動生成。
Ø 委託的例項化和呼叫
聲明瞭委託(實際上是個派生類),需要建立委託的例項(物件),然後呼叫其方法(函式成員)
建立委託例項的基本形式如下:
委託名委託例項名 = new 委託名(匹配方法);
委託名委託例項名 = 匹配方法 ; //等價簡寫
委託例項名的同步呼叫與方法的呼叫:
委託例項名.Invoke(實參列表);
委託例項名(實參列表);
簡單看個例子理解委託的例項化和呼叫
using System;
namespaceCSharpBook.Chapter09
{
delegatevoidD(int x); // 宣告委託
classC
{
publicstaticvoid M1(
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的時候就會依次呼叫d1、d2。每個指向的函式函式的形參列表也都一樣。
<委託型別> <例項化名>+=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. 每一個值引數(非ref或out修飾符的引數)都存在從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型別。
//以上只是委託的基本概念和用法,與事件結合後使用會比較靈活。
//還有泛型委託和泛型事件。。。