C#之委托
從一開(kāi)始接觸C#到現(xiàn)在,委托對(duì)我來(lái)說(shuō)都是一道坎,曾經(jīng)想要避開(kāi),可是每次寫(xiě)、看C#程序都避免不了接觸這玩意兒,每次都會(huì)覺(jué)得很憋屈!所以這幾天想下點(diǎn)苦功夫一下子徹底搞懂它,下面說(shuō)說(shuō)自己的見(jiàn)解吧~~一、什么是委托
對(duì)于這個(gè)詞,一開(kāi)始接觸的時(shí)候很陌生,這也導(dǎo)致了我的畏懼心理,后來(lái)學(xué)習(xí)設(shè)計(jì)模式的時(shí)候知道了委托模型,當(dāng)時(shí)也不怎么搞的明白,現(xiàn)在冷靜下來(lái)自己看看,這里也算入門(mén)了。委托,說(shuō)白了就是一種相當(dāng)于c++函數(shù)指針的東東(如果對(duì)C++函數(shù)指針不熟的話這就等于白說(shuō)了啊,還是看下面的例子吧!)~
首先看一下委托的額聲明例子: public void delegate Delete_Delegate(string args);委托的聲明中有兩個(gè)東西是非常重要的,第一個(gè)是void,也就是這個(gè)委托可以注冊(cè)(代表)的函數(shù)的返回值,其次便是args,這個(gè)是該委托可以注冊(cè)(代表)的函數(shù)所使用的參數(shù)。換句話說(shuō),所有的以string為參數(shù),以void為返回值(這里暫時(shí)這么說(shuō))的函數(shù)都是可以通過(guò)這個(gè)委托進(jìn)行注冊(cè)的!
我想看到這個(gè)大家就應(yīng)該很有感觸了,一個(gè)方法,除了方法名,最能標(biāo)志它的東西無(wú)非就是返回值和參數(shù),至于修飾符,后面再提,所以委托可以說(shuō)是抓住了方法的核心成分,置于方法名么,這個(gè)自然好理解,如果把方法名也通過(guò)委托表現(xiàn)出來(lái),那就不需要委托的存在了,其實(shí)后面還有一個(gè)用處,那就是多播委托~后面再講
二、委托的使用
委托的使用分為三個(gè)步驟:1.委托聲明 2.委托注冊(cè) 3.委托調(diào)用a. 委托聲明,舉個(gè)例子:public void delegate Print(string name);解說(shuō)同上!
b. 委托注冊(cè):現(xiàn)在假設(shè)有一個(gè)函數(shù):
public void speakEnglish(string say){
Console.WriteLine(say);
}
注冊(cè)方法:Print english = new Print(speakEnglish);
解說(shuō):委托其實(shí)可以被看作是一個(gè)特殊的類(lèi),很明顯,這里聲明的就是該類(lèi)的實(shí)例english,我們?cè)僮⒁庖幌逻@里的構(gòu)造函數(shù)所使用的參數(shù),它是一個(gè)方法名,該方法必須和委托聲明的方法簽名一致,否則會(huì)拋出異常;上面只是一種注冊(cè)方式,還有其他便捷的方法~
c. 委托調(diào)用:english("hello"); 這個(gè)時(shí)候控制臺(tái)便會(huì)輸出hello字符,委托調(diào)用成功!
#########################################華麗麗的分割線#############################################
以上所描述的是最最基本的委托使用過(guò)程,接下來(lái)引申開(kāi)來(lái),繼續(xù)深入
三、多播委托
多播委托對(duì)于委托實(shí)現(xiàn)其價(jià)值來(lái)說(shuō)真的是很重要的,比如在事件監(jiān)聽(tīng)中的使用。
多播委托的特點(diǎn):
1)多播委托,顧名思義,就是說(shuō)可以通過(guò)委托一次調(diào)用一個(gè)以上的方法;
2)多播委托聲明時(shí)必須返回void,否則會(huì)拋出異常;
3)多播委托一旦遇到一個(gè)函數(shù)拋出異常,則會(huì)停止執(zhí)行剩余的函數(shù);(這個(gè)是可以解決的)
這里也可以體會(huì)到為什么不需要把方法名通過(guò)委托表現(xiàn)出來(lái)的原因。
下面舉一個(gè)多播委托的例子:延續(xù)上面的例子(注:多播委托和一般委托的聲明并沒(méi)有什么決定性區(qū)別,唯一要注意的就是上面說(shuō)的特點(diǎn)2)
假設(shè)還有一個(gè)方法:
public void speakChinese(string say){
Console.WriteLine(say);
}
下面需要同時(shí)調(diào)用兩個(gè)方法,那么怎么通過(guò)多播委托實(shí)現(xiàn)呢?
Print print = new Print(speakEnlish);
print += speakChinese;
print("Hello, 中國(guó)!");
這樣的話控制臺(tái)上就會(huì)出現(xiàn)兩邊Hello,中國(guó)字符。
解釋?zhuān)?)很明顯,多播委托不管注冊(cè)多少方法,其方法簽名一致的要求是不可以更改的!
2)注冊(cè)的時(shí)候首先聲名委托的實(shí)例,然后可以通過(guò)“+=”運(yùn)算符添加(注冊(cè))更多的方法!同理可知,也可以通過(guò)運(yùn)算符“-=”取消注冊(cè);
3)注冊(cè)完畢后,一旦如同一般委托通過(guò)委托實(shí)例傳入?yún)?shù)則所有注冊(cè)過(guò)的方法都會(huì)接收參數(shù)運(yùn)行一遍!并且是沒(méi)有執(zhí)行順序的!
通過(guò)3)可以解釋為什么返回值必須是void,甚至返回值是同一個(gè)類(lèi)型都不可以!因?yàn)槎嗖ノ兄凶?cè)的方法是一起調(diào)用的,如果有返回值,比如一個(gè)返回1,一個(gè)返回2,那么就會(huì)出現(xiàn)一個(gè)函數(shù)(委托)返回多個(gè)值的情況,總會(huì)出錯(cuò)!所以必須為Void.
到這里,我想具體什么是委托,怎么使用委托基本上比較清楚了,下面我最最關(guān)心的就是到底委托會(huì)被用在哪些地方呢?委托是C#中比較獨(dú)特的一個(gè)技術(shù),存在即是真理,它的優(yōu)點(diǎn)在什么地方?(很不幸,我C++沒(méi)學(xué)好,關(guān)于C++函數(shù)指針的有點(diǎn)也沒(méi)參透,所以不能遷移過(guò)來(lái))
####################################委托的能力######################################
大家在使用集合時(shí),一定使用過(guò)Sort類(lèi)似的方法,這個(gè)方法就是用來(lái)對(duì)集合中的元素進(jìn)行排序的,對(duì)于一般元素,比如int等等,它內(nèi)部就嵌有比較方法在里面,然而,很多時(shí)候我們需要比較的往往不是這么簡(jiǎn)單的東西,很多時(shí)候我們要比較的是我們自己定義的類(lèi)型。而Sort方法中也有一個(gè)構(gòu)造函數(shù),是需要我們自己傳入比較函數(shù)的,這里就是一個(gè)使用委托的絕佳范例。下面引用的例子來(lái)自《C#高級(jí)編程(第6版)》(有興趣的話可以去看看,書(shū)不錯(cuò),就是厚了點(diǎn))
程序1:
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4
5 namespace Wrox.ProCSharp.Delegates
6 {
7 class Employee
8 {
9 private string name;
10 private decimal salary;
11
12 public Employee(string name, decimal salary)
13 {
14 this.name = name;
15 this.salary = salary;
16 }
17
18 public override string ToString()
19 {
20 return string.Format("{0}, {1:C}", name, salary);
21 }
22
23 public static bool CompareSalary(object x, object y)
24 {
25 Employee e1 = (Employee)x;
26 Employee e2 = (Employee)y;
27 return (e1.salary < e2.salary);
28 }
29 }
30 }
這個(gè)Employee類(lèi)里面定義了一個(gè)比較方法,CompareSalary,這個(gè)方法就是等會(huì)兒需要注冊(cè)委托的方法2 using System.Collections.Generic;
3 using System.Text;
4
5 namespace Wrox.ProCSharp.Delegates
6 {
7 class Employee
8 {
9 private string name;
10 private decimal salary;
11
12 public Employee(string name, decimal salary)
13 {
14 this.name = name;
15 this.salary = salary;
16 }
17
18 public override string ToString()
19 {
20 return string.Format("{0}, {1:C}", name, salary);
21 }
22
23 public static bool CompareSalary(object x, object y)
24 {
25 Employee e1 = (Employee)x;
26 Employee e2 = (Employee)y;
27 return (e1.salary < e2.salary);
28 }
29 }
30 }
程序2:
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4
5 namespace Wrox.ProCSharp.Delegates
6 {
7 delegate bool Comparison(object x, object y);
8
9 class BubbleSorter
10 {
11 static public void Sort(object[] sortArray, Comparison comparer)
12 {
13 for (int i = 0; i < sortArray.Length; i++)
14 {
15 for (int j = i + 1; j < sortArray.Length; j++)
16 {
17 if (comparer(sortArray[j], sortArray[i]))
18 {
19 object temp = sortArray[i];
20 sortArray[i] = sortArray[j];
21 sortArray[j] = temp;
22 }
23 }
24 }
25 }
26 }
27 }
這個(gè)類(lèi)里面聲明了一個(gè)委托,可以看到這個(gè)委托和上面Employee類(lèi)里面的方法簽名是一樣的,也就是說(shuō)可以通過(guò)這個(gè)代理注冊(cè)上面的CompareSalary方法2 using System.Collections.Generic;
3 using System.Text;
4
5 namespace Wrox.ProCSharp.Delegates
6 {
7 delegate bool Comparison(object x, object y);
8
9 class BubbleSorter
10 {
11 static public void Sort(object[] sortArray, Comparison comparer)
12 {
13 for (int i = 0; i < sortArray.Length; i++)
14 {
15 for (int j = i + 1; j < sortArray.Length; j++)
16 {
17 if (comparer(sortArray[j], sortArray[i]))
18 {
19 object temp = sortArray[i];
20 sortArray[i] = sortArray[j];
21 sortArray[j] = temp;
22 }
23 }
24 }
25 }
26 }
27 }
程序3:
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4
5 namespace Wrox.ProCSharp.Delegates
6 {
7
8 class Program
9 {
10 static void Main()
11 {
12 Employee[] employees =
13 {
14 new Employee("Bugs Bunny", 20000),
15 new Employee("Elmer Fudd", 10000),
16 new Employee("Daffy Duck", 25000),
17 new Employee("Wiley Coyote", (decimal)1000000.38),
18 new Employee("Foghorn Leghorn", 23000),
19 new Employee("RoadRunner'", 50000)};
20
21 BubbleSorter.Sort(employees, Employee.CompareSalary);
22
23 foreach (var employee in employees)
24 {
25 Console.WriteLine(employee);
26 }
27 //for (int i = 0; i < employees.Length; i++)
28 // Console.WriteLine(employees[i].ToString());
29 }
30 }
31 }
我們看一下這里是怎么使用委托的!2 using System.Collections.Generic;
3 using System.Text;
4
5 namespace Wrox.ProCSharp.Delegates
6 {
7
8 class Program
9 {
10 static void Main()
11 {
12 Employee[] employees =
13 {
14 new Employee("Bugs Bunny", 20000),
15 new Employee("Elmer Fudd", 10000),
16 new Employee("Daffy Duck", 25000),
17 new Employee("Wiley Coyote", (decimal)1000000.38),
18 new Employee("Foghorn Leghorn", 23000),
19 new Employee("RoadRunner'", 50000)};
20
21 BubbleSorter.Sort(employees, Employee.CompareSalary);
22
23 foreach (var employee in employees)
24 {
25 Console.WriteLine(employee);
26 }
27 //for (int i = 0; i < employees.Length; i++)
28 // Console.WriteLine(employees[i].ToString());
29 }
30 }
31 }
首先聲明了一個(gè)Employee數(shù)組,目標(biāo)是對(duì)里面的元素盡心排序,我們看到,排序使用的方法是靜態(tài)的Sort()方法,傳入了一個(gè)數(shù)組和一個(gè)方法名,而在Sort()方法的參數(shù)列表里面大家可以看到,與方法名對(duì)應(yīng)的位置是委托的實(shí)例,也就是說(shuō),我們把比較的方法通過(guò)委托傳給了比較函數(shù),這樣,我們就能夠通過(guò)改變比較的方法來(lái)實(shí)現(xiàn)比較我們自己定義的類(lèi)型的目的!
個(gè)人感覺(jué)這樣的實(shí)現(xiàn)很方便,尤其是將比較函數(shù)分離出來(lái),這樣符合程序的“開(kāi)閉原則”,將需要變化,可能變化的部分剝離了出來(lái)~~
當(dāng)然,委托的能力不是這樣一個(gè)例子可以說(shuō)清楚的,況且這里還沒(méi)有列出多播委托的例子,在窗體事件中每個(gè)多播委托可以實(shí)現(xiàn)一個(gè)事件激發(fā)多個(gè)監(jiān)聽(tīng)者的功能,所以說(shuō)到底委托有什么好處,該怎么使用,都需要多多實(shí)踐才能更多的領(lǐng)悟~
接下來(lái)還有些東西值得侃一侃:
1.委托之匿名方法:
委托不但可以為已經(jīng)存在的方法注冊(cè),而且在編輯過(guò)程中可以直接添加一個(gè)匿名方法,換句話說(shuō),就是臨時(shí)寫(xiě)一個(gè)方法放入注冊(cè)列表中,下面是一個(gè)示例,延續(xù)上面的案例:
Print print = delegate(string say){
Console.WriteLine(say);
}
這里注冊(cè)就不是一個(gè)方法了,而是一個(gè)代碼塊,關(guān)鍵字是delegate,這是不能更改的,后面的傳入?yún)?shù)以及里面方法的返回值(如果有返回值的情況)必須要和委托聲明的一致!
這里我有一個(gè)疑問(wèn),這種匿名方法的委托使用是不是可以用在多播委托中?按常理應(yīng)該是可以添加的,那么怎么取消注冊(cè)呢?(沒(méi)有方法名的情況下是不可能使用-=的嘛~)
2.委托
λ表達(dá)式
λ
表達(dá)式是C#3.0新提供的語(yǔ)法,用于委托類(lèi)型以簡(jiǎn)化代碼,事實(shí)上,就是委托形式的一種符號(hào)表達(dá)形式,下面是一個(gè)示例:
λ
表達(dá)式
Print print => (say) {
Console.WriteLine(say);
}
上面的表達(dá)式很奇怪吧?say這個(gè)變量沒(méi)有實(shí)現(xiàn)聲明就開(kāi)始使用了對(duì)吧?解釋如下:Console.WriteLine(say);
}
其實(shí)say是聲明過(guò)的,在哪里聲明的呢?對(duì),沒(méi)錯(cuò),就是第一次使用的時(shí)候聲明的,但是很明顯,這里根本不像變量聲明啊!根本就沒(méi)有指出類(lèi)型嘛,這就是委托的獨(dú)特之處,在聲明委托的時(shí)候就已經(jīng)指出了參數(shù)的類(lèi)型,在這里表達(dá)式是專(zhuān)門(mén)代替委托而存在的,也就相當(dāng)于在這里指出了say的類(lèi)型!
下面再看一個(gè)例子就知道表達(dá)式和委托的關(guān)系了:
委托聲明:public delegate bool Predicate(int object);
看下面的使用方法:
Predicate p1 = x => x>5;
list.findAll(x => x>5
第二種使用方式相當(dāng)于:
list.findAll()
delegate(int x){return x>5}
對(duì)比上面兩條語(yǔ)句,是否有所感悟?
3.委托之協(xié)變與抗變
這個(gè)我想也不太常遇到,其實(shí)就是委托的參數(shù)與返回值和父類(lèi)與子類(lèi)之間的一種轉(zhuǎn)換,這里就不羅哩羅嗦了~~
寫(xiě)寫(xiě)寫(xiě)寫(xiě)發(fā)現(xiàn)有點(diǎn)像是整理,不過(guò)想說(shuō)的也基本上說(shuō)在這里了,歡迎留下意見(jiàn),也歡迎留下問(wèn)題互相交流~
posted on 2011-09-01 15:30 灰色客棧 閱讀(244) 評(píng)論(0) 編輯 收藏