事件是C#的基礎之一,學好事件對於瞭解.NET框架大有好處。html
事件最多見的比喻就是訂閱,即,若是你訂閱了個人博客,那麼,當我發佈新博客的時候,你就會獲得通知。前端
而這個過程就是事件,或者說是事件運行的軌跡。編程
事件是發散,以個人博客爲核心,向全部訂閱者發送消息。咱們把這種發散稱之爲[多播]。架構
最多見的事件用途是窗體編程,在Windows窗體應用程序和WPF應用程序中。框架
當在窗體中點擊按鈕,移動鼠標等事件時,相應的後臺程序會收到通知,再執行代碼。async
事件的定義函數
官方對事件的說明是這樣的:類或對象能夠經過事件向其餘類或對象通知發生的相關事情。學習
換成正常語言就是,事件能夠定義成靜態的或普通的,因此事件就能夠由聲明的對象調用,也能夠直接經過類調用靜態事件。spa
事件是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使用效果,也讓新的前端設計師逐漸放棄了事件。
因此,事件在將來的編程中,極可能將不在有那麼重要的地位了。但學好事件,對於咱們理解微軟框架,仍是有很大幫助的。
----------------------------------------------------------------------------------------------------
注:此文章爲原創,任何形式的轉載都請聯繫做者得到受權並註明出處!
若您以爲這篇文章還不錯,請點擊下方的【推薦】,很是感謝!