1. 程式人生 > >C#事件(event)解析

C#事件(event)解析

  事件(event),這個詞兒對於初學者來說,往往總是顯得有些神祕,不易弄懂。而這些東西卻往往又是程式設計中常用且非常重要的東西。大家都知道windows訊息處理機制的重要,其實C#事件就是基於windows訊息處理機制的,只是封裝的更好,讓開發者無須知道底層的訊息處理機制,就可以開發出強大的基於事件的應用程式來。

先來看看事件程式設計有哪些好處。
在以往我們編寫這類程式中,往往採用等待機制,為了等待某件事情的發生,需要不斷地檢測某些判斷變數,而引入事件程式設計後,大大簡化了這種過程:
- 使用事件,可以很方便地確定程式執行順序。
- 當事件驅動程式等待事件時,它不佔用很多資源。事件驅動程式與過程式程式最大的不同就在於,程式不再不停地檢查輸入裝置,而是呆著不動,等待訊息的到來,每個輸入的訊息會被排進佇列,等待程式處理它。如果沒有訊息在等待,則程式會把控制交回給作業系統,以執行其他程式。
- 事件簡化了程式設計。作業系統只是簡單地將訊息傳送給物件,由物件的事件驅動程式確定事件的處理方法。作業系統不必知道程式的內部工作機制,只是需要知道如何與物件進行對話,也就是如何傳遞訊息。

  有了這麼多好處,看來我們的確有必要掌握它。俗話說:“難了不會,會了不難”。就讓我們一步一步開始吧...

  要講事件,必然要講到委託(delegate)。它們之間的關係可以通過一個淺顯的比方來說明,這個比方可能不是十分恰當。比如你要租一個房屋,這是一個事件,那麼委託就是房屋租賃中介,當你把租房子的訊息告知中介後,中介就會產生出一套符合你要求的房屋租賃方案來。再由中介執行這套方案,你便租得了這個房屋,即事件被處理了。當然你也可以不通過中介,直接找房東,但如果沒有網際網路等工具,你如何得到誰出租房屋的資訊?話題扯遠了。

委託(delegate)
委託可以理解成為函式指標,不同的是委託是面向物件,而且是型別安全的。關於委託的理解,可以參考我的另一篇文章

《C#委託之個人理解》

事件(event)
  我們可以把事件程式設計簡單地分成兩個部分:事件發生的類(書面上叫事件發生器)和事件接收處理的類。事件發生的類就是說在這個類中觸發了一個事件,但這個類並不知道哪個個物件或方法將會加收到並處理它觸發的事件。所需要的是在傳送方和接收方之間存在一個媒介。這個媒介在.NET Framework中就是委託(delegate)。在事件接收處理的類中,我們需要有一個處理事件的方法。好了,我們就按照這個順序來實現一個捕獲鍵盤按鍵的程式,來一步一步說明如何編寫事件應用程式。

1、首先建立一個自己的EventArgs類。
引自MSDN:
EventArgs是包含事件資料的類的基類,此類不包含事件資料,在事件引發時不向事件處理程式傳遞狀態資訊的事件會使用此類。如果事件處理程式需要狀態資訊,則應用程式必須從此類派生一個類來儲存資料。


因為在我們鍵盤按鍵事件中要包含按鍵資訊,所以要派生一個KeyEventArgs類,來儲存按鍵資訊,好讓後面知道按了哪個鍵。

internalclass KeyEventArgs : EventArgs
{
    
privatechar keyChar;
    
public KeyEventArgs( char keyChar ) : base()
    
{
        
this.keyChar = keyChar;
    }


    
publicchar KeyChar
    
{
        
get
        
{
            
return keyChar;
        }

    }

}


2、再建立一個事件發生的類KeyInputMonitor,這個類用於監控鍵盤按鍵的輸入並觸發一個事件:

internalclass KeyInputMonitor
{
    
// 建立一個委託,返回型別為void,兩個引數
publicdelegatevoid KeyDown( object sender, KeyEventArgs e );
    
// 將建立的委託和特定事件關聯,在這裡特定的事件為OnKeyDown
publicevent KeyDown OnKeyDown;

    
publicvoid 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 );
                    
// 觸發事件
                    OnKeyDown( this, keyEventArgs );
                    
break;
            }

        }
while!finished );
    }

}


這裡注意OnKeyDown( this, KeyEventArgs );一句,這就是觸發事件的語句,並將事件交由KeyDown這個委託來處理,委託指定事件處理方法去處理事件,這就是事件接收方的類的事情了。引數this是指觸發事件的物件就是本身這個物件,keyEventArgs包含了按鍵資訊。

3、最後建立一個事件接收方的類,這個類先產生一個委託例項,再把這個委託例項新增到產生事件物件的事件列表中去,這個過程又叫訂閱事件。然後提供一個方法回顯按鍵資訊。

internalclass EventReceiver
{
    
public EventReceiver( KeyInputMonitor monitor )
    
{
        
// 產生一個委託例項並新增到KeyInputMonitor產生的事件列表中
        monitor.OnKeyDown +=new KeyInputMonitor.KeyDown( this.Echo );
    }

    
privatevoid Echo(object sender, KeyEventArgs e)
    
{
        
// 真正的事件處理函式
        Console.WriteLine( "Capture key: {0}", e.KeyChar );
    }

}


4、看一下如何呼叫

publicclass MainEntryPoint
{
    
publicstaticvoid 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();
        }
    }
不當之處,歡迎指正。