C# 知識回顧 - 委托 delegate
【博主】反骨仔 【原文】http://www.cnblogs.com/liqingwen/p/6031892.html
目錄
What's 委托
delegate 一種自定義的引用類型,它包含了特定的參數列表和返回類型。
使用委托時,只需要對應的方法的簽名和返回類型兼容即可,無論是實例方法,抑或是靜態方法。通過調用委托的實例就相當於調用方法本身,因為委托存儲的是一個方法列表,調用委托的實例就相當於依次調用方法列表的內容。委托它將方法作為參數進行傳遞給了其它方法,我們常用的事件處理程序就是通過委托調用的方法,也是一種觀察者模式的體現。
下面的示例演示了一個委托聲明:
public delegate int Del(int x, int y);
使用委托的要求是:方法簽名與返回類型兼容。可以是靜態方法,也可以是實例方法。
委托的特點
-
類型安全,類似於 C 和 C++ 中的函數指針。
-
可將方法作為參數進行傳遞。
-
可用於定義回調方法。
-
委托可以鏈接在一起;例如,可以對一個事件調用多個方法。
-
方法不必與委托類型完全匹配。
使用委托
委托,一種類型,它是安全的,自定義的,委托的名稱就決定了這個委托是什么類型。
//該委托可以封裝 “,參數類型 string,返回類型 void” 的方法 public delegate void MyDel(string message);
委托的實例對象通常使用兩種方式進行構建,直接使用類的方法名,或者使用 Lambda 表達式,當然匿名方法也可以。
在調用委托的時刻,我們將傳遞到委托的參數會繼續傳遞到委托列表的方法中。如果委托列表中包含返回值的話,會將最后一個返回值返回給調用方。也就是該委托對象調用完畢的返回值。
1 //該委托名為 MyDel,可以封裝 “參數類型 string,返回值類型 void” 的方法 2 public delegate void MyDel(string message); 3 4 class Program 5 { 6 static void Main(string[] args) 7 { 8 //實例化委托 9 MyDel del = Print; 10 //調用委托 11 del("Hi"); 12 13 Console.Read(); 14 } 15 16 /// <summary> 17 /// 打印文本 18 /// </summary> 19 /// <remarks>這是一個可用於 MyDel 委托的方法</remarks> 20 /// <param name="message"></param> 21 private static void Print(string message) 22 { 23 Console.WriteLine(message); 24 } 25 }

委托的關鍵字是 delegate,它派生自 Delegate 類,也是 sealed,即密封類,不能作為基類再繼續派生。
異步回調:允許以方法的形式作為參數形式進行傳遞,並在稍后進行該委托的調用。通過這個形式使用的委托,調用方不需要知道方法的具體實現,只是簡單的把它當做一個功能即可,這類似接口的封裝。
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 MyDel del = Print; 6 CallbackMethod(100, 150, del); //將委托傳遞到 CallbackMethod 方法 7 8 Console.Read(); 9 } 10 11 /// <summary> 12 /// 回調方法 13 /// </summary> 14 /// <param name="m"></param> 15 /// <param name="n"></param> 16 /// <param name="del"></param> 17 private static void CallbackMethod(int m, int n, MyDel del) 18 { 19 del((m + n).ToString()); 20 } 21 22 private static void Print(string message) 23 { 24 Console.WriteLine(message); 25 } 26 }

在這里的 CallbackMethod 作用是,調用委托,因為它包含的是 Print() 方法的調用,所以只需要傳遞對應的 string 類型作為參數即可。
我們在創建委托的時候,你可以選擇使用的是實例方法或者是靜態方法。當你使用的是實例方法時,該委托對象會同時引用該實例的對象及它的方法。委托並不關心應用引用對象的類型,它關心的是,方法簽名和返回值兼容,即可。不過,如果你創建委托對象包含的是靜態方法的時候,它是只引用該方法的。
使用 += 可以把多個方法添加到一個委托對象的調用列表中,調用一次委托,相當於一次性調用一堆方法。
1 //該委托可以封裝 “名 MyDel,參數類型 string,返回值類型 void” 的方法 2 public delegate void MyDel(string message); 3 4 class MyClass 5 { 6 public void Print1(string message) 7 { 8 Console.WriteLine($"{message} - {nameof(Print1)}"); 9 } 10 11 public void Print2(string message) 12 { 13 Console.WriteLine($"{message} - {nameof(Print2)}"); 14 } 15 } 16 17 class Program 18 { 19 static void Main(string[] args) 20 { 21 var myClass = new MyClass(); 22 MyDel del1 = myClass.Print1; 23 MyDel del2 = myClass.Print2; 24 MyDel del3 = Print; 25 26 var del = del1 + del2; 27 del += del3; //這里使用 += 28 del("Hi!"); 29 30 Console.Read(); 31 } 32 33 private static void Print(string message) 34 { 35 Console.WriteLine($"{message} - {nameof(Print)}"); 36 } 37 }

委托對象 del,他內部存儲的是一個包含三個方法的調用列表(Print1、Print2 和 Print),在你調用 del 對象時,調用列表中的方法會依次調用。
多播委托:一個委托對象調用多個方法,使用 +=。
若要從委托對象的調用列表中移除方法,需要使用 -=。
1 static void Main(string[] args) 2 { 3 var myClass = new MyClass(); 4 MyDel del1 = myClass.Print1; 5 MyDel del2 = myClass.Print2; 6 MyDel del3 = Print; 7 8 var del = del1 + del2; 9 del += del3; //使用 += 10 del("Hi!"); 11 12 Console.WriteLine("======分割線======"); 13 14 del -= del2; //使用 -= 15 del("Hi!"); 16 17 Console.Read(); 18 }

你也可以編寫一些方法獲取調用列表中方法的數量:
1 static void Main(string[] args) 2 { 3 var myClass = new MyClass(); 4 MyDel del1 = myClass.Print1; 5 MyDel del2 = myClass.Print2; 6 MyDel del3 = Print; 7 8 var del = del1 + del2; 9 del += del3; //使用 += 10 //del("Hi!"); 11 12 var count = del.GetInvocationList().Length; //獲取委托調用列表中方法的數量 13 Console.WriteLine(count); 14 15 Console.WriteLine("======分割線======"); 16 17 del -= del2; //使用 -= 18 //del("Hi!"); 19 20 count = del.GetInvocationList().Length; //獲取委托調用列表中方法的數量 21 Console.WriteLine(count); 22 23 Console.Read(); 24 }

多播委托派生自 MulticastDelegate,也是繼承自 Delegate的,常用於事件處理中。
傳送門
【參考】https://msdn.microsoft.com/zh-cn/library/windows/apps/ms173171(v=vs.120).aspx
【參考】微軟官方文檔
