事件(event),這個詞兒對於初學者來說,往往總是顯得有些神秘,不易弄懂。而這些東西卻往往又是編程中常用且非常重要的東西。大家都知道windows消息處理機制的重要,其實C#事件就是基於windows消息處理機制的,只是封裝的更好,讓開發者無須知道底層的消息處理機制,就可以開發出強大的基於事件的應用程序來。 先來看看事件編程有哪些好處。 在以往我們編寫這類程序中,往往采用等待機制,為了等待某件事情的發生,需要不斷地檢測某些判斷變量,而引入事件編程后,大大簡化了這種過程: - 使用事件,可以很方便地確定程序執行順序。 - 當事件驅動程序等待事件時,它不占用很多資源。事件驅動程序與過程式程序最大的不同就在於,程序不再不停地檢查輸入設備,而是呆着不動,等待消息的到來,每個輸入的消息會被排進隊列,等待程序處理它。如果沒有消息在等待,則程序會把控制交回給操作系統,以運行其他程序。 - 事件簡化了編程。操作系統只是簡單地將消息傳送給對象,由對象的事件驅動程序確定事件的處理方法。操作系統不必知道程序的內部工作機制,只是需要知道如何與對象進行對話,也就是如何傳遞消息。 有了這么多好處,看來我們的確有必要掌握它。俗話說:“難了不會,會了不難”。就讓我們一步一步開始吧... 要講事件,必然要講到委托(delegate)。它們之間的關系可以通過一個淺顯的比方來說明,這個比方可能不是十分恰當。比如你要租一個房屋,這是一個事件,那么委托就是房屋租賃中介,當你把租房子的消息告知中介后,中介就會產生出一套符合你要求的房屋租賃方案來。再由中介執行這套方案,你便租得了這個房屋,即事件被處理了。當然你也可以不通過中介,直接找房東,但如果沒有互聯網等工具,你如何得到誰出租房屋的信息?話題扯遠了。 委托(delegate) 委托可以理解成為函數指針,不同的是委托是面向對象,而且是類型安全的。關於委托的理解,可以參考我的另一篇文章《C#委托之個人理解》。 事件(event) 我們可以把事件編程簡單地分成兩個部分:事件發生的類(書面上叫事件發生器)和事件接收處理的類。事件發生的類就是說在這個類中觸發了一個事件,但這個類並不知道哪個個對象或方法將會加收到並處理它觸發的事件。所需要的是在發送方和接收方之間存在一個媒介。這個媒介在.NET Framework中就是委托(delegate)。在事件接收處理的類中,我們需要有一個處理事件的方法。好了,我們就按照這個順序來實現一個捕獲鍵盤按鍵的程序,來一步一步說明如何編寫事件應用程序。 1、首先創建一個自己的EventArgs類。 引自MSDN: EventArgs是包含事件數據的類的基類,此類不包含事件數據,在事件引發時不向事件處理程序傳遞狀態信息的事件會使用此類。如果事件處理程序需要狀態信息,則應用程序必須從此類派生一個類來保存數據。 因為在我們鍵盤按鍵事件中要包含按鍵信息,所以要派生一個KeyEventArgs類,來保存按鍵信息,好讓后面知道按了哪個鍵。 internal class KeyEventArgs : EventArgs { private char keyChar; public KeyEventArgs( char keyChar ) : base() { this.keyChar = keyChar; } public char KeyChar { get { return keyChar; } } } 2、再創建一個事件發生的類KeyInputMonitor,這個類用於監控鍵盤按鍵的輸入並觸發一個事件: internal class KeyInputMonitor { // 創建一個委托,返回類型為void,兩個參數 public delegate void KeyDownHandler( object sender, KeyEventArgs e ); // 將創建的委托和特定事件關聯,在這里特定的事件為KeyDown public event KeyDownHandler KeyDown; public void Run() { bool finished = false; do { Console.WriteLine( "Input a char" ); string response = Console.ReadLine(); char responseChar = ( response == "" ) ? ' ' : char.ToUpper( response[0] ); switch( responseChar ) { case 'X': finished = true; break; default: // 得到按鍵信息的參數 KeyEventArgs keyEventArgs = new KeyEventArgs( responseChar ); // 觸發事件 KeyDown( this, keyEventArgs ); break; } }while( !finished ); } } 這里注意KeyDown( this, KeyEventArgs );一句,這就是觸發事件的語句,並將事件交由KeyDownHandler這個委托來處理,委托指定事件處理方法去處理事件,這就是事件接收方的類的事情了。參數this是指觸發事件的對象就是本身這個對象,keyEventArgs包含了按鍵信息。 3、最后創建一個事件接收方的類,這個類先產生一個委托實例,再把這個委托實例添加到產生事件對象的事件列表中去,這個過程又叫訂閱事件。然后提供一個方法回顯按鍵信息。 internal class EventReceiver { public EventReceiver( KeyInputMonitor monitor ) { // 產生一個委托實例並添加到KeyInputMonitor產生的事件列表中 monitor.KeyDown += new KeyInputMonitor.KeyDownHandler( this.OnKeyDown ); } private void OnKeyDown(object sender, KeyEventArgs e) { // 真正的事件處理函數 Console.WriteLine( "Capture key: {0}", e.KeyChar ); } } 4、看一下如何調用 public class MainEntryPoint { public static void Start() { // 實例化一個事件發送器 KeyInputMonitor monitor = new KeyInputMonitor(); // 實例化一個事件接收器 EventReceiver eventReceiver = new EventReceiver( monitor ); // 運行 monitor.Run(); } } 總結: C#中使用事件需要的步驟: 1.創建一個委托 2.將創建的委托與特定事件關聯(.Net類庫中的很多事件都是已經定制好的,所以他們也就有相應的一個委托,在編寫關聯事件處理程序--也就是當有事件發生時我們要執行的方法的時候我們需要和這個委托有相同的簽名) 3.編寫事件處理程序 4.利用編寫的事件處理程序生成一個委托實例 5.把這個委托實例添加到產生事件對象的事件列表中去,這個過程又叫訂閱事件 C#中事件產生和實現的流程: 1.定義A為產生事件的實例,a為A產生的一個事件 2.定義B為接收事件的實例,b為處理事件的方法 3.A由於用戶(程序編寫者或程序使用者)或者系統產生一個a事件(例如點擊一個Button,產生一個Click事件) 4.A通過事件列表中的委托對象將這個事件通知給B 5.B接到一個事件通知(實際是B.b利用委托來實現事件的接收) 6.調用B.b方法完成事件處理 public class A { public delegate void EventHandler(object sender); public event EventHandler a; public void Run() { Console.WriteLine("Trigger an event."); a(this); } } class B { public B(A a) { a.a += new A.EventHandler(this.b); } private void b(object sender) { Console.WriteLine("Received and handled an event." ); Console.Read(); } }