委託
1、委託可確保回調方法是型別安全的;
2、委託可以順序調用多個方法,並支援靜態方法和執行個體方法;
3、委託封裝執行個體方法是非常有用的,因為對象內部的代碼可以訪問對象的執行個體方法成員,這就意味著對象可以維護一些狀態,並且在回調方法執行期間用到這種狀態;
4、委託實際上是類,因為編譯器會為委託產生一個完整的類,所以在可以定義類的地方,就可以定義委託;
5、Delegate類定義了兩個唯讀公用執行個體屬性:Target和Method;Target返回一個對象引用,該對象就是方法回調時要操作的那個對象,如果委派物件封裝了一個靜態方法,那麼Target將返回Null;Method屬性返回一個System.Reflection.MethodInfo對象引用(標識回調方法);
協變與反協變
協變(convariance):指的是一個方法返回從委託的傳回型別派生的一個類型;
反協變(contra-variance):指的是一個方法的參數類型可以是委託的參數類型的基底類型;
internal delegate object MyCallback(int s);
public class Delegate_covariance_contra_variance
{
private static string SomeMethod(object s)
{
Console.WriteLine(s.ToString());
return s.ToString();
}
public static void Test()
{
//Error:“SomeMethod(object)”的重載均與委託“MyCallback”不匹配
MyCallback call = new MyCallback(SomeMethod);
call(1);
}
上面的代碼編譯時間會出錯,分析一下:
首先,SomeMethod的傳回型別(String)是繼承自委託傳回型別(Object),所以這種協變是允許的;
其次,SomeMethod的參數類型(Object)是委託的參數類型(int)的基類,符合
反協變的定義,但是這種反協變是無法通過編譯的,因為協變與反協變都不支援實值型別和Void:因為實值型別和void的儲存結構式變化的,而參考型別的儲存結構始終是一個指標;
最後,修改方法就是將委託的參數類型改為參考型別。
internal delegate object MyCallback(string s);
對委託鏈調用進行更多控制
委託鏈調用要注意兩個問題:
1、 除最後一個傳回值之外,回調方法的所有傳回值都會被丟棄;
2、 如果被調用的委託中有一個拋出了異常或者堵塞了相當長一段時間,就會停止調用後續所有對象。
為此,MulticastDelegate類提供了一個執行個體方法GetInvocationList,
private static String GetComponentStatusReport(GetStatus status) {
// If the chain is empty, there抯is nothing to do.
if (status == null) return null;
// Use this to build the status report.
StringBuilder report = new StringBuilder();
// Get an array where each element is a delegate from the chain.
Delegate[] arrayOfDelegates = status.GetInvocationList();
// Iterate over each delegate in the array.
foreach (GetStatus getStatus in arrayOfDelegates) {
try {
// Get a component's status string, and append it to the report.
report.AppendFormat("{0} {1} {2}", getStatus(),getStatus.Target+":"+getStatus.Method, Environment.NewLine);
}
catch (InvalidOperationException e) {
// Generate an error entry in the report for this component.
Object component = getStatus.Target;
report.AppendFormat(
"Failed to get status from {1}{2}{0} Error: {3}{0}{0}",
Environment.NewLine,
((component == null) ? "" : component.GetType() + "."),
getStatus.Method.Name, e.Message);
}
}
// Return the consolidated report to the caller.
return report.ToString();
}
C#為委託提供的文法便利
主要介紹了委託的簡便寫法,這裡有一個定義匿名方法(anonymous method),要注意三點:
1、 在編寫匿名方法的時候,在代碼中的delegate關鍵字後加入這個指定名稱(Object obj);
internal sealed class AClass {
private static void CallbackWithoutNewingADelegateObject() {
ThreadPool.QueueUserWorkItem(
delegate(Object obj) { Console.WriteLine(obj); }, 5);
}
}
2、 匿名方法標識為private,這會禁止在類型內部定義的任何代碼訪問這個方法;
匿名方法標識為static,這是因為代碼沒有訪問任何執行個體方法成員,不過,代碼可以引用類中定義的靜態欄位或靜態方法;
internal sealed class AClass {
private static string m_str;
private static void CallbackWithoutNewingADelegateObject() {
ThreadPool.QueueUserWorkItem(
delegate(Object obj) { Console.WriteLine( m_str+":"+obj); }, 5);
}
}
3、 如果CallbackWithoutNewingADelegateObject方法不是靜態,那麼匿名方法的代碼就可以包含對執行個體成員的引用;
internal sealed class AClass {
private int m_i;
private void CallbackWithoutNewingADelegateObject1()
{
ThreadPool.QueueUserWorkItem(
delegate(Object obj) { Console.WriteLine(m_i + ":" + obj); }, 5);
}
}