委託是多播委託,我們可以通過"+="把多個方法賦給委託變數,這樣就形成了一個委託鏈。本篇的話題包括:委託鏈是怎樣形成的,如何呼叫委託鏈方法,以及委託鏈異常處理。
□ 呼叫返回型別為void的委託所形成的委託鏈方法
□ 呼叫返回型別不是void的委託所形成的委託鏈方法
□ 呼叫返回型別不是void的泛型委託所形成的委託鏈方法
□ 呼叫Func<T>泛型委託所形成的委託鏈方法
□ 呼叫Action<T>泛型委託所形成的委託鏈方法
□ 處理委託鏈異常
呼叫返回型別為void的委託所形成的委託鏈方法
來看下面的例子:
namespace ConsoleApplication3
{internal delegate void MySayDel(string msg);class Program
{static void Main(string[] args){MySayDel del = SayHello;del = (MySayDel)Delegate.Combine(del, new MySayDel(SayNice)); //等同於:del += SayNice;del += SayOk;del("darren");
}static void SayHello(string msg){Console.WriteLine("hello " + msg);
}static void SayNice(string msg){Console.WriteLine("nice " + msg);
}static void SayOk(string msg){Console.WriteLine("ok " + msg);
}}}
最後,呼叫委託執行方法,最先註冊的方法最先執行。"+="是一種"語法糖",內部其實呼叫了Delegate的靜態方法Combine,形成委託鏈,再把委託鏈賦給委託變數。大致如下:
→當執行MySayDel del = SayHello;
→當執行del = (MySayDel)Delegate.Combine(del, new MySayDel(SayNice)),在託管堆上又建立MySayDel委託例項指向SayNice方法,接著複製原先的、指向SayHello方法的委託例項,2個委託例項形成委託鏈,即藍色區域部分,棧上的委託變數del指向委託鏈。
呼叫返回型別不是void的委託所形成的委託鏈方法
以上,委託的返回型別是void,當呼叫委託的時候,依次執行委託鏈的方法。可是,如果委託的返回型別不是void,會不會依次執行委託鏈的方法呢?
internal delegate int MyCalulateDel(int val1, int val2);class Program
{static void Main(string[] args){MyCalulateDel del = Add;del += Sub;Console.WriteLine(del.Invoke(20, 10));}static int Add(int val1, int val2){return val1 + val2;
}static int Sub(int val1, int val2){return val1 - val2;
}}
以上,當呼叫委託不會依次執行委託鏈方法,而是會執行最後註冊的方法。
如果我們想得到所有委託方法的返回結果,該如何做到呢?
--委託為我們提供了一個GetInvocationList的例項方法,可以獲取所有委託。
internal delegate int MyCalulateDel(int val1, int val2);class Program
{static void Main(string[] args){MyCalulateDel del = Add;del += Sub;var result = GetResultForEachDel(del, 20, 10);foreach (var item in result){Console.WriteLine(item);}}static List<int> GetResultForEachDel(MyCalulateDel del, int val1, int val2){List<int> result = new List<int>();foreach (MyCalulateDel item in del.GetInvocationList()){result.Add(item.Invoke(val1, val2));}return result;
}static int Add(int val1, int val2){return val1 + val2;
}static int Sub(int val1, int val2){return val1 - val2;
}}
以上,通過GetInvocationList例項方法獲取所有的委託,然後分別呼叫所有的委託方法。
呼叫返回型別不是void的泛型委託所形成的委託鏈方法
以上委託只針對int型別,如果不想把型別"寫死",就應該使用泛型委託。
namespace ConsoleApplication5
{internal delegate T MyGenericDel<T>();class Program
{static void Main(string[] args){MyGenericDel<int> d = ReturnOne;
d += ReturnTwo;var result = GetReturnValues(d);foreach (var item in result){Console.WriteLine(item);}}//執行所有的泛型委託
static IEnumerable<TModel> GetReturnValues<TModel>(MyGenericDel<TModel> d)
{//遍歷委託鏈
foreach (MyGenericDel<TModel> del in d.GetInvocationList()){yield return del.Invoke();
}}static int ReturnOne(){return 1;
}static int ReturnTwo(){return 2;
}}}
泛型委託,一般是在返回型別名稱後面、方法名稱後面,形參型別名稱後面加上佔位符<T>。
呼叫Func<T>泛型委託所形成的委託鏈方法
而實際上,對於泛型委託,.NET為我們準備了Func<T>,它有多個過載方法:
最後一個形參是返回型別,其餘形參是輸入引數。
class Program
{static void Main(string[] args){Func<int> d = ReturnOne;
d += ReturnTwo;var result = GetReturnValues(d);foreach (var item in result){Console.WriteLine(item);}}//執行所有的泛型委託
static IEnumerable<TModel> GetReturnValues<TModel>(Func<TModel> d)
{//遍歷委託鏈
foreach (Func<TModel> del in d.GetInvocationList()){yield return del();
}}static int ReturnOne(){return 1;
}static int ReturnTwo(){return 2;
}}
呼叫Action<T>泛型委託所形成的委託鏈方法
如果一個泛型委託沒有返回值,就可以使用Action<T>,它有多個過載方法:
所有的形參都是輸入引數,沒有返回值。
class Program
{static void Main(string[] args){Action<string> action = SayOnce;
action += SayTwice;action.Invoke("darren");
}static void SayOnce(string str){Console.WriteLine("我只說一次" + str);
}static void SayTwice(string str){Console.WriteLine("我第一次說" + str);
Console.WriteLine("我第二次說" + str);
}}
處理委託鏈異常
在委託鏈中,如果任何一個委託方法丟擲異常,如何處理呢?
--需要遍歷委託鏈,讓每個委託單獨執行,並編寫處理異常程式碼
class Program
{static void Main(string[] args){Action<string> action = SayOnce;
action += SayTwice;foreach (Action<string> a in action.GetInvocationList()){try
{a("darren");
}catch (Exception)
{Console.WriteLine("有異常");
}}}static void SayOnce(string str){Console.WriteLine("我只說一次" + str);
}static void SayTwice(string str){Console.WriteLine("我第一次說" + str);
Console.WriteLine("我第二次說" + str);
throw new Exception();}}
總結:
○ 如果委託的返回型別是void,並且形成委託鏈,只要呼叫委託就會依次執行委託鏈方法。
○ 如果委託的返回型別不是void,並且形成委託鏈,可以使用委託的GetInvocationList例項方法獲取所有委託,然後遍歷這些委託依次執行委託方法得到返回型別。
○ 泛型委託優先考慮使用Func<T>和Action<T>,如果有返回型別使用Func<T>,如果返回型別為void使用Action<T>
○ 委託鏈的異常處理思路是:遍歷委託鏈中的每個委託,針對每個委託編寫捕獲異常的程式碼
“委託、Lambda表示式、事件系列”包括: