1. 程式人生 > >初步理解委託、事件、匿名方法和Lambda

初步理解委託、事件、匿名方法和Lambda

最經在學習LinqtoSql,然後扯到Lambda表示式,然後扯到匿名方法,然後扯到委託,最後扯到事件處理。。。後來發現對委託這個概念和事件處理這個過程理解得不是很清晰,遂得一下學習筆記。那裡說得不對,請大家多多指教!

第一部分:理解委託

委託委託,顧名思義,就是類似於中間人的意思,有些事情你不做,委託別人去做,比如你想相親,但你不想去主動約女孩子,那你可以交給媒婆去幫你約。

如果你學過C++,請把委託理解成函式指標,都是為了呼叫函式。函式指標可以呼叫符合該函式指標要求的函式。什麼叫符合該函式指標要求?就是被呼叫的函式擁有和該函式指標一樣的返回型別、引數的個數相同、對應引數的型別一致。

函式指標舉例說明。int (*p)(int,int);定義了一個函式指標p,p只能呼叫 函式原型(或者說函式簽名)是 返回型別為int、只有兩個引數並且引數型別都是int的函式。

委託舉例說明:delegate void MyDelegate(int a,int b);聲明瞭一種委託型別 MyDelegate(委託有不同型別,就好像中間人有不同型別,你想相親就找媒婆這種中間人,你想看風水,就找風水大師這種中間人),這種委託型別只能呼叫 返回型別是void、只有兩個引數並且引數型別都是int的函式。跟函式指標是不是很像?

委託其實就是一個類,委託的基類是Delegate 類,只不過我們不能顯示地從 Delegate

 類派生出委託類(Delegate 類不是委託型別,它的派生類才是),這是給系統和編譯器用的。只不過類例項化之後就叫物件,而委託例項化之後還是叫委託而已,另外例項化委託必須提供一個相對應的函式作為引數。

1、定義一個函式Max,函式簽名位:返回型別為int,兩個引數,引數型別都是int

int Max(int x,int y)  {return x>y?x:y;}

2、建立一個委託型別(名字是MyDelegate),並宣告該委託型別可以呼叫的函式的函式原型(函式簽名)為:返回型別為int,兩個引數,引數型別都是int。注意,委託是一個類,建立委託型別要放在函式外面

delegate int MyDelegate(int a,int b);

3.建立一個委託類的例項(或者說MyDelegate的例項),並指向要呼叫的方法,有兩種方式:

//利用委託類的構造方法指定,這是最為常見的一種方式

MyDelegate md = new MyDelegate(Max);

//利用自動推斷方式來指明要呼叫的方法,該形式更型別於函式指標

MyDelegate md = Max;

用第一種方式建立委託例項時必須提供一個函式簽名符合該委託型別的函式作為引數。

4、利用委託類例項呼叫所指向的方法

int c = md(4,5);

總結:可以把委託理解成函式指標,可以呼叫函式簽名和委託型別一致的函式

第二部分:事件處理(本文主要想表達對委託的理解,所以這部分以後再詳說)

事件處理涉及到兩個物件:一個是事件源(就是觸發該事件的物件),另一個是事件接收者(提供了處理方法的類)。兩者本無聯絡。

事件處理的過程:首先事件源觸發了某事件,委託捕獲到這件事(委託通過訂閱該事件實現),委託呼叫事件接收類中的相應函式進行處理。

舉例說明:this.button1.Click += new System.EventHandler(this.button1_Click);

事件源是button1,觸發的事件是 按鈕單擊事件 Click,委託是 System.EventHandler ,事件處理函式是 button1_Click 。上面程式碼的意思是,System.EventHandler (委託)對 this.button1(事件源)的  Click (事件) 進行訂閱,當this.button1 的Click事件發生時,委託接收事件源傳過來的一些引數,接著呼叫 this.button1_Click (事件處理函式) 並把從事件源那裡接收的引數傳給該函式作為引數。

第三部分:匿名方法

匿名方法就是沒有名字的方法(函式),既然沒有名字,就是說只有在定義的時候能呼叫,在其他地方就不能呼叫了(沒有名字啊,那就找不到嘛)。為什麼要用到匿名方法呢?呼叫函式是需要花銷的,但有時候呼叫的一些方法很短小(例如一句話方法)、只完成很少的功能,這個時候就有點得不償失了,此時就可以定義一個匿名方法來完成這個功能了,而匿名方法作為內聯程式碼,花銷相對小很多。匿名方法都是和委託連在一起用的(以後還有lambda表示式),以前建立委託例項時需要傳遞一個函式名給它作為引數,現在可以通過匿名方法直接把一段程式碼傳進去了

定義匿名方法:用到delegate關鍵字,如:delegate(int a, int b){return a > b ? a : b ;}   程式碼定義了一個匿名方法,該方法的引數是 int a 和 int b ,方法體是 {return a > b ? a : b}。如果只是定義一個匿名方法沒有意義,因為定義完過後你就再也不能用這個匿名方法,所以匿名方法要在定義的時候就馬上使用。一般來說就是初始化委託了。

使用匿名方法:

View Code 複製程式碼
 1 namespace DelegateTest
 2 {
 3     class Program
 4     {
 5         //建立委託型別 6         delegate int Mydelegate(int x, int y);
 7         static void Main(string[] args)
 8         {
 9           //建立委託例項,指向匿名函式10           Mydelegate mydelegate = delegate(int x,int y) { return x + y; };
11 
12           //通過委託呼叫匿名方法13           Console.WriteLine(mydelegate(3,2));
14 
15           Console.ReadKey();
16         }
17     }
18 }
複製程式碼

 總結:匿名方法就是沒有名字的方法,只能通過委託來被呼叫(這句話可能有誤,匿名方法也可以直接訂閱事件作為事件處理函式,當事件發生時就接收相應引數並執行函式體)。

第四部分:Lambda

上面說到可以通過匿名方法以內聯程式碼的方式來簡介地實現委託,但還有更簡潔的方法,那就是用lambda表示式來代替匿名方法,在這裡,lambda表示式就是匿名方法的簡潔版。

Lambda表示式的語法如下:

(param1, param2 ...,paramN) =>

{

表示式1;

表示式2;

return 返回值;

}

param1, param2 ...,paramN 就是引數,不用確定型別,編譯器會做這個工作,花括號了就是lambda表示式要執行的語句,如果對應的委託型別有返回值,那麼就要有return 語句。

把第三部分那個匿名方法的例子改成用lambda表示式實現:

複製程式碼
 1 namespace DelegateTest
 2 {
 3     class Program
 4     {
 5         //建立委託型別 6         delegate int Mydelegate(int x, int y);
 7         static void Main(string[] args)
 8         {
 9           //建立委託例項,指向匿名函式
10 //      Mydelegate mydelegate = delegate(int x,int y) { return x + y; };
11 12 //通過委託呼叫匿名方法
13 //    Console.WriteLine(mydelegate(3,2));14 
15           Mydelegate youdelegate = (x, y) => { return (x - y); };
16           Console.WriteLine(youdelegate(3, 2));
17 
18           Console.ReadKey();
19         }
20     }
21 }
複製程式碼

總結:匿名方法就是沒有名字的函式,Lambda表示式就是匿名方法的簡潔版(指的是用在委託上這方面,Lambda還有其他方面的用處),兩者都只是讓程式碼更簡潔,但在更底層層次本質是一樣的