第十七章 委託
一、 委託的使用
靜態委託和執行個體委託,使用方法類似,這裡給出一個使用可變參數委託的例子:
using System;
public class DelCls
{
public delegate void DelDef(params string[] strParams);
public static void CallDel(DelDef dd)
{
if(dd != null) //請務必在此處進行判斷,這是個好習慣
{
dd("Hello", "world");
}
}
}
public class DelIns
{
//聲明為private(私人)成員並不影響在類型內部使用委託
private static void ClsCallStr(params string[] strParams) //類型方法
{
//將字串數組並順序輸出
foreach(string str in strParams)
{
Console.Write("{0} ", str);
}
Console.WriteLine();
}
public void InsCallStr(params string[] strParams) //執行個體方法
{
//將字串數組並反序輸出
for(int i = strParams.Length - 1; i >= 0; i --)
{
Console.Write("{0} ", strParams[i]);
}
Console.WriteLine();
}
public static void Main()
{
DelIns di = new DelIns();
DelCls.DelDef dd = null;
Console.WriteLine("combine two delegate:");
dd += new DelCls.DelDef(DelIns.ClsCallStr);
dd += new DelCls.DelDef(di.InsCallStr);
DelCls.CallDel(dd);
Console.WriteLine("remove the first delegate:");
dd -= new DelCls.DelDef(DelIns.ClsCallStr);
DelCls.CallDel(dd);
}
}
/*運行結果
combine two delegate:
Hello world
world Hello
remove the first delegate:
world Hello
*/
在C#中使用委託方法:
l 建立委託所使用的方法必須和委託聲明相一致(參數列表、傳回值都一致)
l 利用 +=、-=來進行委託的連結或取消連結或直接使用Delegate.Combine和Delegate.Remove方法來實現
l 使用MulticastDelegate的執行個體方法GetInvocationList()來擷取委託鏈中所有的委託
二、 委託揭秘
所有的委託都繼承自MulticastDelegate,編譯器在編譯時間刻為委託的聲明產生了一個完整的委託類,重點注意其中的一些成員:
ü 建構函式,傳入委託的目標對象(執行個體)及指向回調方法的整數
ü 繼承自MulticastDelegate的_target(System.Object)欄位
ü 繼承自MulticastDelegate的_methodPtr(System.Int32)欄位
ü 繼承自MulticastDelegate的_prev(System.MulticastDelegaet)欄位
ü 產生的與方法聲明相一致Invoke函數用以調用方法
可利用MulticastDelegate中的Method及Target屬性來考察_methodPtr及_target欄位的性質。
關於編譯器產生的委託類及Invoke方法的調用情況,可通過使用ILDAsm.exe查看執行檔案的IL代碼獲得
將上例中類型DelIns中的Main方法作如下修改,以實驗GetInvocationList及MulticastDelegate中屬性的使用:
public class DelIns
{
…
public static void Main()
{
…
Delegate[] arrDel = dd.GetInvocationList();
foreach(DelCls.DelDef d in arrDel)
{
Console.WriteLine("Object type: {0}, Method name: {1}",
(d.Target != null) ? d.Target.GetType().ToString() : "null",
d.Method.Name);
}
…
}
…
}
/*運行結果
…
Object type: null, Method name: ClsCallStr
Object type: DelIns, Method name: InsCallStr
…
*/
三、 委託判等
首先判斷_methodPtr及_target欄位是否相等,若不等則返回false;
若相等,繼續判斷_prev是否為null(指向委託鏈頭部的委託),若為null,則相等返回true;
若不等,繼而判斷委託鏈上所有委派物件,重複上述步驟。
可見牽涉到委託鏈的時候是個遞迴判斷的過程。
四、 委託鏈
l 首先被加入到委託鏈中的委託位於委託鏈的尾部,但首先被調用,這是因為Invoke中利用遞迴對委託函數進行調用,這樣位於頭部的委託最後被調用。
l 委託調用後的傳回值,只是最後一次被呼叫者法的傳回值,即委託鏈頭部委託的傳回值
l 每調用一次Remove方法只刪除匹配的第一個委託鏈
五、 委託與反射
以下是.net framework sdk文檔提供的Delegate.CreateDelegate方法列表:
建立指定類型的委託以表示指定的靜態方法。
[C#] public static Delegate CreateDelegate(Type, MethodInfo);
建立指定類型的委託,該委託表示要對指定的類執行個體調用的指定執行個體方法。
[C#] public static Delegate CreateDelegate(Type, object, string);
建立指定類型的委託,該委託表示指定類的指定靜態方法。
[C#] public static Delegate CreateDelegate(Type, Type, string);
建立指定類型的委託,該委託表示要按指定的大小寫敏感度對指定類執行個體調用的指定執行個體方法。
[C#] public static Delegate CreateDelegate(Type, object, string, bool);
下面的樣本示範了建立靜態方法委託、執行個體方法委託以及動態調用委託:
using System;
using System.Reflection;
public class DelReflection
{
public delegate void GoGo(string strPam, Int32 nPam);
public static void ClsGo(string strPam, Int32 nPam)
{
Console.WriteLine("In class, String:{0}, Int32:{1}", strPam, nPam);
}
public void InsGo(string strPam, Int32 nPam)
{
Console.WriteLine("In instance, String:{0}, Int32:{1}", strPam, nPam);
}
public static void Main()
{
Delegate d = null;
d = Delegate.CreateDelegate(typeof(GoGo), typeof(DelReflection), "ClsGo");
if(d != null)
d.DynamicInvoke(new Object[]{"Hello", 45});
DelReflection dr = new DelReflection();
d = Delegate.CreateDelegate(typeof(GoGo), dr, "InsGo");
if(d != null)
d.DynamicInvoke(new Object[]{"Hello", 45});
}
}
/*運行結果
In class, String:Hello, Int32:45
In instance, String:Hello, Int32:45
*/