.NET進階篇-語言章-2-Delegate委託、Event事件

知識只有經過整理才能形成技能

整個章節分佈簡介請查看第一篇

內容目錄

一、概述

二、解析委託知識點

1、委託本質

2、委託的使用

3、委託意義

邏輯解耦,減少重複代碼

代碼封裝支持擴展

匿名方法和Lambda表達式

異步多線程

多播委託

三、事件

四、總結

一、概述

先說下委託,委託我們也經常用到。詳盡瞭解委託是必要的,不然在異步多線程的編程中會一頭霧水。委託本質就是一個類,和我們平常定義的類沒多大區別。只是這個類的作用的是描述一些方法,沒有數據成員。一個委託定義了一類擁有同樣返回類型和參數的方法規範。委託的聲明語法就是一個沒有方法體的方法前面加上delegate關鍵字。既然本質是一個類,那它就可以在任何可以定義普通類的位置來定義委託。委託是一個能把方法作爲參數傳遞的對象

事件就簡單了,事件就是委託的一個實例

二、解析委託知識點

1、委託本質。

在VS中編碼中,聲明委託後,會發現委託的着色提示和類時一樣。

但好像不是很有說服力。高級語法都做了很好的封裝,方便編碼人員。.NET的二次編譯,第一次編譯成IL中間語言,中間語言也是一種編程語言,只是它不像高級語言那麼方便人類閱讀。我們可以通過一些工具(像ILSpy)反編譯來窺探下它的內部邏輯。

如圖中紅框所示,我們定義的普通類MyDelegate和委託類型NoReturnPara(繼承自MulticastDelegate)是一致的,都是class。在委託類型NoResultNoPara中也有.ctor(在IL中構造函數),此外還有我們以後會經常用到的Invoke方法和BeginInvoke、EndInvoke方法,前者是同步調用,後者是異步調用

2、委託的使用

我們使用委託一般就是三步走,第一步定義委託,第二部聲明委託實例,第三部調用。定義委託就像上面所示在一個沒有方法體的方法前加上delegate關鍵字即可。它給定了一種約束,只能用規定的方法結構(返回值和參數)的實例化委託。

public delegate void NoReturnNoPara();//1 聲明委託
NoReturnNoPara method = new NoReturnNoPara(this.DoNothing);//2 委託的實例化
method.Invoke();//3 委託實例的調用
private void DoNothing()
{
        Console.WriteLine("This is DoNothing");
}

委託的實例實際就是代表你綁定的方法,把方法包裝成一個變量,Invoke時自動執行,這樣用委託實例的調用就和直接方法調用效果一樣。那這樣有什麼用呢?用處太大了。這樣意味着你可以把一個方法當做參數傳遞給另一個方法,這麼說好像也體會不到。類比下我們泛型的用途,泛型用於設計和類型無關的對象功能。那委託就是用於設計和方法無關的,可以將方法行爲(邏輯)在從外部注入到對象內部。

3、委託意義

邏輯解耦,減少重複代碼

假如有以下集合List,Student有身高、年齡屬性。我們需要寫一個方法查詢出身高超過180cm的學生。(先不用List的Find、Where等方法,他們參數都是需要委託的,我們先考慮不用委託實現)。我們的方法可能像下面這樣

然後如果需要查詢出年齡大於20歲的呢,增加參數?方法重載?顯然都要破壞原有類的封裝。那我們想到“甩鍋”,把變化的邏輯甩給調用者。上面兩種查詢也只是student.Height>180和student.Age>20判斷邏輯的不同,可以把這部分當做參數傳遞進來。那麼一組邏輯包裝成變量,這就是委託。一個方法委託了我,你調用我就是調用那個方法。改造後的方法像下面這樣

這樣我們只需要在外部調用的地方修改或增加相應的邏輯方法就可以。這裏就相當於自己實現了List的Where擴展方法,這也是它的內部原理。

但這樣是不是也挺繁瑣的,還要定義委託、定義個方法,然後實例化委託,好繁瑣。既然委託規定了方法規範,那如果方法裏不依賴的具體的類型,我們隨意指定方法的參數類型該多好,還記得上節說的泛型嗎。這裏就使用泛型委託,我們也不自己去定義了,.NET爲我們封裝了通用的兩類泛型委託Action<T>和Func<T>,前者代表無返回值,後者代表有返回值,每一類泛型委託都有十幾個泛型參數可以指定,絕對夠你用了。我們基本不用自己定義委託。這樣還不夠,我們還是要定義方法的呀。

代碼封裝,支持擴展

既然委託實例就是一個方法,結合泛型,那我們還可以做些更有趣的事情,下面實例代碼的左右就是給一個方法增加了異常處理。(這只是無返回值的,你也可以加一個有返回值的)。這樣就做到了,如果你的方法是指定給委託的,那麼就可以捕獲異常,你大可以不在具體方法內處理異常(實際,還是老老實實處理,這只是最後一道門)。我們甚至可以在調用任何方法前後加上日誌,而不用修改原類的封裝。有點類似AOP(面向切片編程)的味道了。

匿名方法和Lambda表達式

有時候簡單的邏輯我們不必編寫一個指定的方法。在實例化委託時可以直接指定一個匿名方法。像下面這樣。優點當然是減少代碼的複雜度了,還可以訪問匿名方法外部的變量,但匿名方法內部不能使用break,continue等跳轉語句。用來做簡單的邏輯。

在C#3.0開始,我們有了Lambda表達式代替匿名方法,它比匿名方法更加簡單。Lambda運算符“=>”(發音goesto)的左邊列出了需要的參數,右邊是利用該參數方法的實現代碼。如果表達式只有一個語句,還可以像圖二那樣簡寫。

異步多線程

異步多線程的實現都是基於委託的。開篇我們看到委託有BeginInvoke和EndInvoke,這裏先簡單介紹下,我們會在後面異步多線程編程中詳細解讀。簡單的代碼如下圖所示,結果會發現BeginInvoke實際是啓用一個線程來調用方法的

BeginInvoke方法觸發你的異步方法,它和你想要執行的異步方法有相同的參數。另外還有兩個可選參數,第一個是AsyncCallback委託是異步完成的回調方法。第二個是用戶自定義對象,該對象將傳遞到回調方法中。BeginInvoke立即返回並且不等待完成異步的調用(繼續執行該下面的代碼,不需要等待)。BeginInvoke返回IAsyncResult接口,可用於檢測異步調用的過程。通過EndInvoke方法檢測異步調用的結果。如果異步調用尚未完成,EndInvoke將阻塞調用線程,直到它完成。

多播委託

前面使用的每個委託都只包含一個方法的調用。調用委託的次數和調用方法的次數相同。如果要調用多個方法,就需要多次顯示調用這個委託(就像多次調用一個方法一毛一樣)。委託也可以包含多個方法。這種委託稱爲多播委託,但要注意如果方法有返回值,則只能得到委託調用最後一個方法的結果

使用運算符“+=”、“-=”來增加或去除委託的方法。+= 爲委託實例按順序增加方法,形成方法鏈,Invoke時,按順序依次執行。-= 爲委託實例移除方法,從方法鏈的尾部開始匹配,遇到第一個完全吻合的,移除且只移除一個,沒有也不異常。但有一點要注意,多播委託時是不能使用異步的。

三、事件

事件也幾乎無處不在,它提供一種發佈/訂閱機制。我們做桌面開發,Button類提供的Click事件。觸發Click事件時調用的方法需要定義,其參數類型由委託類型定義。事件就是帶event關鍵字的委託的實例,event可以限制變量被外部調用/直接賦值。事件的標準用法如下,(當然你也可以自己定義更有意義的委託類型來代替Action)。

事件就是委託的實例,所以事件能幹的事情,普通的委託實例也都能幹。只是爲了某些場景下,事件規範了使用方法。如,事件只能用過+=來註冊方法,只能在方法外部聲明在內部調用,普通委託實例多用於回調,而事件多用於外部接口。後面設計模式中的觀察者模式就是其典型應用。

四、總結

其實感覺理的不是很細,主要還是知識點的掃盲鞏固,再往細了去總結可能就會車軲轆話反反覆覆的了。
委託事件,我們平常會很常用的。特別是如果進階一下,異步多線程編程時會用的更多,沒有委託就沒有異步多線程。委託就是描述一類方法的類型,委託的實例就是代表一個方法。我們把一個方法當做一個委託的實例就可以進行傳遞,對於解耦有奇效。事件是委託的實例,最終是用來完成某一業務邏輯的一部分,只是這部分會變化,那麼就把變化的形成封裝出去,交給上層來指定,通過事件可以提供一個供外部擴展動作的接口,這樣就會更加的靈活。規定動作我內部寫死,擴展的交給外部。

如果手機在手邊,也可以關注下vx:xishaobb,互動或獲取更多消息。當然這裏也一直更新de。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章