C#語法——事件,逐漸邊緣化的大哥。


事件是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使用效果,也讓新的前端設計師逐漸放棄了事件。

所以,事件在未來的編程中,很可能將不在有那么重要的地位了。但學好事件,對於我們理解微軟框架,還是有很大幫助的。

C#語法——元組類型

C#語法——泛型的多種應用

C#語法——await與async的正確打開方式

C#語法——委托,架構的血液

我對C#的認知。

----------------------------------------------------------------------------------------------------

注:此文章為原創,任何形式的轉載都請聯系作者獲得授權並注明出處!
若您覺得這篇文章還不錯,請點擊下方的推薦】,非常感謝!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM