委托和事件是.Net 架構的重要組成部分,在GUI程式開發中,大量使用了事件處理,但是親們,對于委托,我們是否還記得曾經在書上看到的詳細内容。委托的使用注意事項是什麼?我們會使用委托和事件,但是我們不了解事件背後的原理,親們,我們忘記委托了嗎?反正我是忘記了。
委托是方法調用的指針,也稱為函數指針,是一種特殊的對象類型,包含的是方法的位址。注意是位址,在.Net 中,委托不同于c++中的函數指針,在C#中 委托是類型安全的操作,這意味着什麼呢?這意味着我們定義的方法簽名必須和委托的定義類型以及傳回值一緻,否則會出現編譯錯誤的提示資訊。
如何定義一個委托:
delegate int AddDelegate(int x,int y); 委托的定義和類的定義一緻,在任何可以定義類的地方都可以定義委托,另外,委托 的實作是繼承自MulticastDelegate 類的,也就是說定義一個委托,基本上等價于定義一個新類。委托類似于方法的定義,但是沒有方法體,隻有方法簽名,另外在方法簽名的前面添加了delegate關鍵字。
定義一個簡單的委托執行個體:
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 Test t = new Test();
6 t.Execute(3, 5);
7 Console.Read();
8 }
9 }
10
11 public class Test
12 {
13 /// <summary>
14 /// 在類的内部定義了一個委托
15 /// </summary>
16 /// <param name="x"></param>
17 /// <param name="y"></param>
18 /// <returns></returns>
19 delegate int AddDelegate(int x, int y);
20 /// <summary>
21 /// 執行個體方法Add1 和委托具有相同的方法簽名
22 /// </summary>
23 /// <param name="x"></param>
24 /// <param name="y"></param>
25 /// <returns></returns>
26 public int Add1(int x, int y)
27 {
28 Console.WriteLine("Add1計算得到的值為" + (x + y));
29 return x + y;
30 }
31 /// <summary>
32 /// 靜态方法Add2 和委托具有相同的方法簽名
33 /// </summary>
34 /// <param name="x"></param>
35 /// <param name="y"></param>
36 /// <returns></returns>
37 public static int Add2(int x, int y)
38 {
39 Console.WriteLine("Add2計算得到的值為" + (x + y * 2));
40 return x + y * 2;
41 }
42
43 public void Execute(int x, int y)
44 {
45 AddDelegate addDelegate = new AddDelegate(Add1);
46 addDelegate += Add2; //通過+= 多點傳播委托 ,表示一個委托執行個體具有多個方法,多點傳播委托不能保證方法的執行順序。
47 addDelegate(x, y);
48 }
49 /// <summary>
50 /// 多點傳播委托在其中一個調用方法出現異常時會停止疊代,我們優先采用下面方法,即逐個的調用 方法清單執行,可以避免由于某個調用方法出現異常,導緻其他調用方法無法執行的問題
51 /// </summary>
52 /// <param name="x"></param>
53 /// <param name="y"></param>
54 public void Execute3(int x, int y)
55 {
56 AddDelegate addDelegate = new AddDelegate(Add1);
57 addDelegate += Add2; //通過+= 多點傳播委托 ,表示一個委托執行個體具有多個方法,多點傳播委托不能保證方法的執行順序。
58 if (addDelegate != null)
59 {
60 Delegate[] addArr = addDelegate.GetInvocationList();//擷取多點傳播委托的調用清單
61 if (addArr != null)
62 {
63 foreach (var item in addArr)
64 {
65 ((AddDelegate)item)(x, y);
66 }
67 }
68 }
69 }
70 public void Execute1(int x, int y)
71 {
72 AddDelegate addDelegate = Add1;//直接為委托執行個體指派方法簽名 稱為委托推斷 .Net 編譯器會識别委托簽名
73 addDelegate += Add2;
74 addDelegate(x, y);
75 }
76 }
執行個體總結:
- addDelegate =Add1 這種為委托執行個體指派的方式稱為 委托推斷,是由.Net 編譯器自動推斷方法簽名是否符合委托。
- 委托可以通過+= 來實作一個委托執行個體具有多個調用方法執行,請注意,多點傳播委托的執行不能保證順序,即不能保證方法注冊的次序和調用次序一緻。2.多點傳播委托在執行過程中,如果某個調用方法出現異常,則整個多點傳播委托的調用方法清單都會停止執行,我們可以采用Execute3 展示的那樣,逐個手動執行調用方法清單,避免由于某個調用方法出現異常,導緻其他調用方法不會執行的問題。
- 委托執行個體的方法即可以是執行個體方法,又可以是靜态方法,隻要保證方法簽名一緻就可以正常的調用。
- 委托可以作為參數傳遞,下面我們會展示一個通過将委托作為執行個體,來實作排序的功能。
- 委托可以具有通路修飾符public、protected以及private等。
- 可以通過委托的簡便方式,匿名方法來直接定義一個方法到委托執行個體,.Net 編譯器會幫助我們生成一個随機的方法名稱,隻是我們不需要。
- lambda 也可以應用到委托中,通過lambda表達式生成的是匿名方法,或者說是匿名委托。
我們來定義一個将委托作為參數傳遞,實作排序的功能。目前我們的需求是建立一個通用類庫,其中有一個可以将數組中的内容按升序排列,但是數組的資料類型我們可以自定義,也就是說,我們不能指定數組的資料類型為某一種具體類型,可以為int、double ,也可以為類。
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 //Test t = new Test();
6 //t.Execute(3, 5);
7 List<string> listInt = new List<string>();
8
9 List<Employee> listEmployee = new List<Employee>();
10 for (int i = 10; i >= 0; i--)
11 {
12 listInt.Add(i.ToString());
13 Employee employee = new Employee()
14 {
15 Name = i.ToString(),
16 Salary = i
17 };
18 listEmployee.Add(employee);
19 }
20 Test1 test1 = new Test1();
21 Employee[] arrEmployee = listEmployee.ToArray();
22 test1.Sort(arrEmployee, test1.CompareEmployee);
23
24 foreach (var item in arrEmployee)
25 {
26 Console.WriteLine(item.Salary);
27 }
28
29 string[]arrInt=listInt.ToArray();
30 test1.Sort(arrInt, test1.CompareInt);
31 foreach (var item in arrInt)
32 {
33 Console.WriteLine(item);
34 }
35
36
37
38 Console.Read();
39 }
40 }
41 public class Test1
42 {
43
44 /// <summary>
45 /// 委托 比較兩個值的大小 x>y 傳回true
46 /// </summary>
47 /// <param name="x"></param>
48 /// <param name="y"></param>
49 /// <returns></returns>
50 public delegate bool Compare(object x, object y);
51
52 public void Sort(object[] arrObj, Compare compare)
53 {
54 for (int i = 0; i < arrObj.Length; i++)
55 {
56 for (int j = i+1; j < arrObj.Length; j++)
57 {
58 if (compare(arrObj[i], arrObj[j]))
59 {
60 object temp = arrObj[i];
61 arrObj[i] = arrObj[j];
62 arrObj[j] = temp;
63 }
64 }
65 }
66 }
67
68
69 public bool CompareEmployee(object em1, object em2)
70 {
71 if (em1 != null && em2 != null)
72 {
73 return ((Employee)em1).Salary > ((Employee)em2).Salary;
74 }
75 return false;
76 }
77
78 public bool CompareInt(object x, object y)
79 {
80 return Convert.ToInt32(x) > Convert.ToInt32(y);
81 }
82
83 }
84 /// <summary>
85 /// 員工類
86 /// </summary>
87 public class Employee
88 {
89 /// <summary>
90 /// 員工姓名
91 /// </summary>
92 public string Name { get; set; }
93 /// <summary>
94 /// 員工工資
95 /// </summary>
96 public int Salary { get; set; }
97 }
總結:
- 将委托作為參數進行傳遞,實作了泛型委托可以實作的功能。
- 可以在子類實作具體的功能,然後傳遞到對應的方法執行,可以有效的實作可擴充性,同時避免對其他功能的幹擾。
委托作為事件的基礎,本身 隻具有簡單的一些概念,但是我覺得要真正的在實際項目中可以靈活的使用,還是需要深入思考,做個好夢
有人的地方就有政治,有政治就有鬥争,但我讨厭政治,讨厭無休止的迎合。