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:
           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方法

          程序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 }
          我們看一下這里是怎么使用委托的!
          首先聲明了一個(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í)上,
          λ
          表達(dá)式
          就是委托形式的一種符號(hào)表達(dá)形式,下面是一個(gè)示例:

          上面的匿名方法的例子是可以這么用表達(dá)式寫(xiě)的:
              Print print => (say) {
                  Console.WriteLine(say);
              }
          上面的表達(dá)式很奇怪吧?say這個(gè)變量沒(méi)有實(shí)現(xiàn)聲明就開(kāi)始使用了對(duì)吧?解釋如下:
          其實(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)  編輯  收藏


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          <2011年9月>
          28293031123
          45678910
          11121314151617
          18192021222324
          2526272829301
          2345678

          導(dǎo)航

          統(tǒng)計(jì)

          常用鏈接

          留言簿

          隨筆檔案

          文章檔案

          搜索

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 大理市| 乌审旗| 大化| 金乡县| 绥江县| 明溪县| 禄丰县| 故城县| 邳州市| 新河县| 陆丰市| 万全县| 手游| 河池市| 同德县| 泾源县| 孙吴县| 靖州| 林芝县| 涪陵区| 莱阳市| 盘山县| 木兰县| 刚察县| 桑植县| 凤阳县| 阿克苏市| 镇巴县| 洮南市| 宁南县| 博野县| 安阳县| 治多县| 兴城市| 诸城市| 灵川县| 阳江市| 马关县| 瓮安县| 温泉县| 广西|