事件是C#的基礎之一,學好事件對於瞭解.NET框架大有好處。 事件最常見的比喻就是訂閱,即,如果你訂閱了我的博客,那麼,當我發佈新博客的時候,你就會得到通知。 而這個過程就是事件,或者說是事件運行的軌跡。 事件是發散,以我的博客為核心,向所有訂閱者發送消息。我們把這種發散稱之為[多播]。 最常見的事 ...
事件是C#的基礎之一,學好事件對於瞭解.NET框架大有好處。
事件最常見的比喻就是訂閱,即,如果你訂閱了我的博客,那麼,當我發佈新博客的時候,你就會得到通知。
而這個過程就是事件,或者說是事件運行的軌跡。
事件是發散,以我的博客為核心,向所有訂閱者發送消息。我們把這種發散稱之為[多播]。
最常見的事件用途是窗體編程,在Windows窗體應用程式和WPF應用程式中。
當在窗體中點擊按鈕,移動滑鼠等事件時,相應的後臺程式會收到通知,再執行代碼。
事件的定義
官方對事件的說明是這樣的:類或對象可以通過事件向其他類或對象通知發生的相關事情。
換成正常語言就是,事件可以定義成靜態的或普通的,所以事件就可以由聲明的對象調用,也可以直接通過類調用靜態事件。
事件是C#中的一種類型,除了框架為我們定義好的事件外,我們還可以自定義事件,用event關鍵字來聲明。
下麵我們來看最基礎的事件定義。
public delegate void TestDelegate(string message); public event TestDelegate testEvent;
我們首先定義了一個委托,然後利用event關鍵字,定義一個事件。
整體上看,好像就是在定義一個委托,只是在委托的定義之前,加了個event關鍵字。
沒錯,事件的定義就是這樣,因為要聲明一個事件,需要兩個元素:
一,標識提供對事件的響應的方法的委托。
二,一個類,用存儲事件的數據。即,事件要定義在類中。
下麵我們來為這個事件賦值。
public void Init() { testEvent += new TestDelegate(EventSyntax_testEvent); testEvent += EventSyntax_testEvent; } private void EventSyntax_testEvent(string message) { Console.WriteLine(message); }
如代碼所示,我們使用了+=這個符號來為事件賦值,賦值的內容是一個委托和一個函數。
其中+=我們將他理解為【添加】。
代碼中,我們使用兩種賦值模式,但實際上都是為事件testEvent添加一個委。
第二種將函數直接【添加】到事件中,編譯時也會把函數轉換成委托【添加】到事件中。
系統提供事件
C#的框架都很經典,而每個經典框架都為我們提供了一些經典事件。
由於事件必須[標識響應方法的委托],所以這些事件所使用的委托都有一個共同的特點,命名中包含Event。
比如EventHandler,CancelEventHandler,RoutedEventHandler,ContextMenuEventHandler等。
其中最經典的就是EventHandler和RoutedEventHandler。
EventHandler:
EventHandler定義如下
[SerializableAttribute] [ComVisibleAttribute(true)] public delegate void EventHandler( object sender, EventArgs e )
他包含了兩個參數,即當我們為事件添加EventHandler委托後,再去觸發該事件;被觸發的委托將得到object sender和EventArgs e兩個參數。
sender:代表源,即觸發該事件的控制項。
e:代表事件參數,即觸發該事件後,事件為被觸發的委托,傳遞了一些參數,以方便委托在處理數據時,更便捷。
根據這個原理,我們可以分析出很多東西。
比如,當控制項DataGrid的事件被觸發時,只要查看一下sender的真實類型,就可以知道,到底是DataGrid觸發的事件,還是DataGridRow或DataGridCell觸發的了。
RoutedEventHandler:
RoutedEventHandler即路由事件,他的定義如下
public delegate void RoutedEventHandler( Object sender, RoutedEventArgs e )
RoutedEventHandler也為我們提供了sender和e兩個參數。
但RoutedEventHandler特別之處是,他的sender並不一定是真實的源,因為他是一個冒泡路由事件,即上升事件。
這裡如果大家有好奇心去看官方文檔,那麼會在相關的介紹中看到兩個單詞sender和source。
通過這兩個單詞,我們會清晰的瞭解路由事件。簡單描述一下sender和source,它們一個是發送者,一個是源。
在EventHandler中,sender即source,因為它是直接事件。而在冒泡事件中,sender不一定等於source。即發送者不一定是源。
下麵我們用WPF來看看路由事件。
我們首先在XAML頁面定義一個RadioButton按鈕,然後設置他的模板是Button。然後分別定義各自的Click方法。
Xaml頁面如下:
<RadioButton Click="btnParent_Click"> <RadioButton.Template> <ControlTemplate> <StackPanel> <TextBlock Text="我的名字" ></TextBlock> <Button Content="Kiba518" Click="btnClild_Click" ></Button> </StackPanel> </ControlTemplate> </RadioButton.Template> </RadioButton>
cs文件事件如下:
private void btnParent_Click(object sender, RoutedEventArgs e) { string type = sender.GetType().ToString();//RadioButton } private void btnClild_Click(object sender, RoutedEventArgs e) { string type = sender.GetType().ToString();//Button }
運行起來,我們點擊按鈕,通過斷點我們可以看到,我們點擊的按鈕觸發了btnClild_Click和btnParent_Click事件。
順序是先btnClild_Click後btnParent_Click。
通過獲取sender的類型,我也可以看到,btnClild_Click的sender類型是Button,而btnParent_Click的sernder類型是RadioButton。
事件驅動編程
事件驅動編程這個概念給我的感覺很怪,因為一直用C#,而C#的很多框架都是事件驅動的,所以一直覺得事件驅動是理所當然。
而當事件驅動設計這個詞經常出現後,反而感覺怪怪的。
就好像,天天吃大米飯,突然有一天,所有人都說大米飯好香的感覺一樣,你一聽就感覺怪怪的。
因為事件驅動對於C#開發而言,實在太普通了。當然,這也得益於微軟框架做的實在是太好了。
所以,我也不知道如何在C#里講事件驅動編程。因為使用C#的框架就是使用事件驅動編程。
事件和委托到底是什麼關係?
事件是用來多播的,並且用委托來為事件賦值,可以說,事件是基於委托來實現的。
但委托中也有多播,那為什麼要單獨弄出來一個事件呢?
首先,存在即合理,事件一定有他存在的意義。
事件存在的意義
我對事件存在的意義是這樣理解的。我們在C#編寫框架時,幾乎不用委托的多播,因為委托的多播和事件存在嚴重的二義性。雖然編寫框架的人學會了使用委托的多播,但使用框架的同事可能並還不太熟練,而且C#框架中,大多是使用事件來進行多播的。
所以委托的多播和事件一起使用的框架,會造成使用這個框架的初級開發者很多困惑,而這種困惑,會產生很多不必要的問題。
比如, 你定義了一個委托,另一個開發者用這個委托做了個多播,當第三個開發者來維護這段代碼時,如果他是新手,不瞭解委托的多播,那就很有可能只修改了委托調用的代碼。而沒有去同步多播這個委托的代碼。那系統就產生了隱藏的bug。
那麼,事件和委托到底是什麼關係呢?
事件與委托的確存在千絲萬縷的關係,怎麼講都是正確的。但,C#開發者只需要記住,他們倆沒關係即可。在C#事件是事件,委托是委托。兩者就如同int和string一樣,沒有任何關係。
原因很簡單,學習的過程中儘量降低概念混淆。而且,在C#開發中,好的架構者也通常會將事件和委托分離,所以,就認為事件和委托沒有關係即可。
結語
其實事件很好理解,一點不複雜。我在寫這篇文章的過程中,也沒想到什麼特別的或者說比較高級的用法。
但真實的應用場景中,我的感覺是,隨著MVVM的成長,事件其實在被逐漸拋棄。雖然微軟做了很多經典的事件驅動框架。但那都是過去了。
比如WPF雖然支持事件驅動,但MVVM在WPF下的表現堪稱完美,所以WPF下的事件幾乎沒有人用了。
再比如前端的Angularjs等框架,提供了優質的MVVM使用效果,也讓新的前端設計師逐漸放棄了事件。
所以,事件在未來的編程中,很可能將不在有那麼重要的地位了。但學好事件,對於我們理解微軟框架,還是有很大幫助的。
----------------------------------------------------------------------------------------------------
註:此文章為原創,歡迎轉載,請在文章頁面明顯位置給出此文鏈接!
若您覺得這篇文章還不錯,請點擊下右下角的【推薦】,非常感謝!
如果您覺得這篇文章對您有所幫助,那就不妨支付寶小小打賞一下吧。