part01.03 委托與 Lambda 表達式(一):委托
delegate 是表示對具有特定參數列表和返回類型的方法的引用類型。
委托最大的作用就是為 類的事件 綁定 事件處理程序
可將任何可訪問類或結構中與委托類型匹配的任何方法分配給委托。該方法可以是靜態方法,也可以是實例方法。這樣便能通過編程方式來更改方法調用,還可以向現有類中插入新代碼。
將方法作為參數進行引用的能力使委托成為定義回調方法的理想選擇。
將方法作為對象封裝起來,允許在運行時間接地綁定一個方法調用。
在一定程度上可以把委托類型看成是定義了一個方法的接口,而委托的實例看成是對這個接口的實現。
實現一個委托很簡單,主要有三個步驟:相關代碼說明
1 /// <summary>View Code2 /// 定義一個委托方法(類型) 3 /// 1.聲明一個 delegate 對象,它應當與你想要傳遞的方法具有 相同的參數和返回值類型 4 /// </summary> 5 /// <param name="i"></param> 6 /// <param name="j"></param> 7 delegate void DelegateMethod(int i, double j); 8 9 /// <summary> 10 /// 一個普通的類 11 /// </summary>12 class SomeClass 13 { 14 public void PrintDigit(int m,double n) 15 { 16 Console.Write(m * n + " "); 17 } 18 19 public static void Func() 20 { 21 var a = new SomeClass(); 22 23 // 使用 SomeClass 的實例方法實例化委托 24 //2.創建 delegate 對象,並將你想要傳遞的函數作為參數傳入 25 // 1) var b=new DelegateMethod(實例名.方法名); 26 // 2) var b=new DelegateMethod(類名.方法名); 27 var b = new DelegateMethod(a.PrintDigit); 28 29 // 此時委托實例執行的是 a.PintDigit 30 // 3.在要實現異步調用的地方,通過上一步創建的對象來調用方法 31 // b(向方法傳遞的參數); 32 b(1, 2.5); 33 34 Console.ReadKey(); 35 } 36 }
委托應用的一個簡單場景:相關代碼說明
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 DelegateClass.ToDelegate(); 6 } 7 } 8 /// <summary> 9 /// 書 10 /// </summary> 11 public class Book 12 { 13 public string Title; 14 public string Author; 15 public decimal Price; 16 public bool Paperback; // 是否是平裝書 17 18 public Book(string title, string author, decimal price, bool paperBack) 19 { 20 Title = title; 21 Author = author; 22 Price = price; 23 Paperback = paperBack; 24 } 25 } 26 27 /// <summary> 28 /// 書籍存儲器 29 /// </summary> 30 public class BookDB 31 { 32 /// <summary> 33 /// 聲明一個用於處理書籍相關數據的委托方法(類型) 34 /// 僅僅說明要處理一本書,如何處理,處理結果等都沒有任何約束 35 /// </summary> 36 /// <param name="book"></param> 37 public delegate void ProcessBookDelegate(Book book); 38 39 // 定義存儲書籍的一個變量集合 40 List<Book> _BookCollection = new List<Book>(); 41 42 /// <summary> 43 /// 添加書籍 44 /// </summary> 45 /// <param name="title"></param> 46 /// <param name="author"></param> 47 /// <param name="price"></param> 48 /// <param name="paperBack"></param> 49 public void AddBook(string title, string author, decimal price, bool paperBack) 50 { 51 _BookCollection.Add(new Book(title, author, price, paperBack)); 52 } 53 54 /// <summary> 55 /// 定義一個處理平裝書的方法 56 /// </summary> 57 /// <param name="processBook">傳入的是一個方法,前面定義的一個委托方法“實例”</param> 58 public void ProcessPaperbackBooks(ProcessBookDelegate processBook) 59 { 60 foreach (Book b in _BookCollection) 61 { 62 if (b.Paperback) 63 { 64 // 調用委托示例來處理每一本書 65 processBook(b); 66 } 67 } 68 } 69 } 70 71 /// <summary> 72 /// 書籍價格計數器 73 /// </summary> 74 public class PriceTotaller 75 { 76 int _CountBooks = 0; // 數量 77 decimal _PriceBooks = 0.0m; // 平均單價 78 79 /// <summary> 80 /// 處理傳入的書籍 81 /// 與前面定義的委托方法 public delegate void ProcessBookDelegate(Book book)的傳入值和返回值都是一樣的,所以,可以把它看成是 ProcessBookDelegate 的實例 82 /// </summary> 83 /// <param name="book"></param> 84 internal void AddBookToTotal(Book book) 85 { 86 _CountBooks += 1; 87 _PriceBooks += book.Price; 88 } 89 90 internal decimal AveragePrice() 91 { 92 return _PriceBooks / _CountBooks; 93 } 94 } 95 96 /// <summary> 97 /// 用於演示說明委托基本應用原理的例子(聲明、實例化和使用委托) 98 /// </summary> 99 public class DelegateClass 100 { 101 public static void ToDelegate() 102 { 103 // 創建書籍數據存儲器 104 BookDB bookDB = new BookDB(); 105 106 // 添加書籍 107 AddBooks(bookDB); 108 109 Console.WriteLine("所有平裝書的書名:"); 110 111 // 打印平裝書名 112 bookDB.ProcessPaperbackBooks(new BookDB.ProcessBookDelegate(PrintTitle)); 113 114 PriceTotaller totaller = new PriceTotaller(); 115 bookDB.ProcessPaperbackBooks(new BookDB.ProcessBookDelegate(totaller.AddBookToTotal)); 116 Console.WriteLine("平裝書的平均價格是:¥{0}", totaller.AveragePrice()); 117 118 Console.ReadKey(); 119 } 120 121 /// <summary> 122 /// 打印書籍的標題 123 /// </summary> 124 /// <param name="book">書籍</param> 125 static void PrintTitle(Book book) 126 { 127 Console.WriteLine(" {0}", book.Title); 128 } 129 130 /// <summary> 131 /// 初始化書籍存儲器的存儲內容 132 /// </summary> 133 /// <param name="bookDB"></param> 134 static void AddBooks(BookDB bookDB) 135 { 136 bookDB.AddBook("C#程序設計", "黃大哥", 70.99m, true); 137 bookDB.AddBook("PHP設計", "黃大哥", 70.99m, true); 138 bookDB.AddBook("計算機操作系統", "黃大哥", 70.99m, false); 139 bookDB.AddBook("C程序設計", "黃大哥", 70.99m, false); 140 } 141 }View Code
委托與接口的比較
下列情況使用委托:
1.當使用事件設計模式時。
2.當封裝靜態方法可取時。
3.當調用方不需要訪問實現該方法的對象中的其他屬性、方法和接口時。
4.需要方便的組合。
5.當類可能需要該方法的多個實現時。
下列情況使用接口:
1.當存在一組可能被調用的相關方法時。
2.當類只需要方法的單個實現時。
3.當使用接口的類想要將該接口強制轉換為其他接口或類類型時。
4.當正在實現的方法鏈接到類的類型或標識時:例如比較方法。
委托的組合操作:
1.可以使用 + 運算符將它們分配給另一個有需要的委托實例。
2.可以使用 - 運算符從組合的委托移除組件委托。
3.組合的委托可調用組成它的那些委托。
4.只有相同類型的委托才可以組合。
相關代碼:
1 class Program 2 { 3 #region 委托的組合使用 4 /// <summary> 5 /// 除了方法語義上表明將傳入的字符串讀出來外,並沒有在代碼上給出任何約束 6 /// </summary> 7 /// <param name="s">待處理字符串</param> 8 delegate void VoiceDelagate(string s); 9 10 #region 這兩個方法都可以作為 VoiceDelegate 的實例 11 public static void Hello(string s) 12 { 13 Console.WriteLine("hello" + s); 14 } 15 16 public static void GoodBye(string s) 17 { 18 Console.WriteLine("goodbye" + s); 19 } 20 21 #endregion 22 23 static void Main(string[] args) 24 { 25 var a = new VoiceDelagate(Hello); 26 var b = new VoiceDelagate(GoodBye); 27 var c = a + b; 28 var d = c - a; 29 30 a("調用委托實例 a 的結果"); 31 b("調用委托實例 b 的結果"); 32 c("C"); 33 d("D"); 34 Console.ReadKey(); 35 } 36 }View Code
委托小結:
1.委托通過特定的返回值和一組輸入參數,將行為方法封裝起來,成為一個特殊的類型,與具有單一方法的接口類似。
2.在聲明為委托類型時,相應的類型簽名決定了哪些方法可以用於創建委托實例,以及哪些簽名可以引用。
3.創建一個委托實例,需要一個方法以及這個調用這個方法的目標場所(實例方法)。
4.委托實例是不可變的。
5.每個委托實例內部都包含一個可供引用操作的列表。
6.委托類型可以混合在一起,也可以從其中一個實例刪除另一個實例。
7.事件不是委托實例,而是配對的 add/remove 方法(猶如類屬性的 getters/setters )
part01.03 委托與 Lambda 表達式(一):委托