.Net邊學(xué)邊講(三)
發(fā)布時(shí)間:2008-07-15 閱讀數(shù): 次 來(lái)源:網(wǎng)樂原科技
談到event,就不能不先說(shuō)一下callback和delegate
如果你使用過C的話,你應(yīng)該知道有一個(gè)函數(shù)叫qsort,是用來(lái)給數(shù)組排序的。但這個(gè)函數(shù)顯然不能承擔(dān)廣泛意義上的比較,因此你需要傳遞一個(gè)指針,他指向具有比較功能的函數(shù)。qsort在每次要比較數(shù)組元素時(shí)都要調(diào)用這個(gè)函數(shù)。這就是callback的概念,在.Net里也可以實(shí)現(xiàn)回調(diào),方法是創(chuàng)建一個(gè)接口,實(shí)現(xiàn)他,傳遞一個(gè)實(shí)現(xiàn)此接口的對(duì)象的引用。delegate呢,你可以將他理解成一個(gè)安全的函數(shù)指針。
Notifications跟callback有點(diǎn)類似,但比簡(jiǎn)單的回調(diào)要復(fù)雜的多。callback意味著要調(diào)用的callback方法被調(diào)用的同時(shí)要調(diào)用的建立callback的方法。這是一個(gè)很緊密的耦合。而Notification則要松散一些,你可以注冊(cè)將來(lái)某段時(shí)間會(huì)或者不會(huì)發(fā)生的Notification,只當(dāng)他們發(fā)生時(shí)處理,否則不用。
你也許想讓你寫的組件當(dāng)一些事情發(fā)生時(shí)通知其他組件,例如,你想寫一個(gè)按鈕組件,當(dāng)你Click的時(shí)候你可能想通知其他組件,而其他組件將不得不準(zhǔn)備向你請(qǐng)求Notification,你就要提供一個(gè)方法告訴他們你已經(jīng)有一個(gè)可用的Notification。另一方面,你可能也是當(dāng)其它組件的一些事情發(fā)生時(shí)希望被通知的人。這時(shí)你就需要找到那個(gè)特定組件可以提供什么Notification。
在.Net中event是一個(gè)你用來(lái)廣播、引發(fā)、處理Notification的機(jī)制。大致是這樣的,可以引發(fā)事件的組件聲明這一事件。而希望處理某一特定組件的某一特定事件的組件通過傳遞一個(gè)方法的delegate向引發(fā)事件的組件注冊(cè)。這樣,當(dāng)事件發(fā)生時(shí)引發(fā)事件的組件就會(huì)調(diào)用每個(gè)已注冊(cè)的方法。通過delegate和event我們可以實(shí)現(xiàn)異步調(diào)用的功能。在C#中是這樣聲明一個(gè)代理的:public delegate void LogHandler(String message);代理在處理這種回調(diào)時(shí)已經(jīng)是很強(qiáng)大了。但是當(dāng)我們需要代理被存儲(chǔ)以便以后的Notification,就有一點(diǎn)麻煩了。比如說(shuō)我們有一個(gè)對(duì)象Button,有一個(gè)Click事件。我們可以聲明一個(gè)ClickHandler 的代理類型用于處理Click事件,在我們的Button的Class中聲明一個(gè)ClickHandler的public實(shí)例,這樣其他組件希望Click發(fā)生時(shí)被通知,就可以簡(jiǎn)單的把他的代理加到Click代理中去。myButton.Click += new ClickHandler(MyMethod);
看上去著好像沒什么問題。但是這里卻存在一個(gè)大問題,我們聲明Click代理是public,這違反了我們以前說(shuō)過的data fields永遠(yuǎn)不要聲明成public,這會(huì)有一系列麻煩。解決的辦法是聲明成private或protected,然后用屬性解決讀寫。這樣我們可以private聲明Click,在寫一對(duì)public方法去增加一個(gè)listener及減少一個(gè)listener。當(dāng)然在.Net中,當(dāng)你聲明一個(gè)event時(shí),.Net已經(jīng)為你做好這一切了。聲明一個(gè)事件:
class AlarmTimer {
public event EventHandler Alarm;
// ...
}
這段代碼說(shuō)明AlarmTimer可以向所有其它對(duì)象廣播它可以引發(fā)一個(gè)叫Alarm的事件。Alarm事件用的是EventHandler代理類型。EventHandler:無(wú)返回值、接受兩個(gè)參數(shù)(Object:指向事件的發(fā)送者,EventArgs:包含關(guān)于事件的數(shù)據(jù))
看一個(gè)例子:
class AlarmTimer {
public event EventHandler Alarm;
private Timer myTimer;
public AlarmTimer() {
myTimer = new Timer();
myTimer.Tick += new EventHandler(OnTick);
}
public void Set(Double seconds) {
myTimer.Interval = (Int32)(seconds * 1000);
myTimer.Start();
}
protected void OnTick(Object sender, EventArgs e) {
myTimer.Stop();
if (Alarm != null) Alarm(this, EventArgs.Empty);
}
public void ReEnable() {
myTimer.Enabled = true;
}
}
注意AlarmTimer既引發(fā)事件(Alarm)又處理事件(Timer中的Tick事件)
static AlarmTimer myAlarm = new AlarmTimer();
public static void TestEvent() {
myAlarm.Alarm += new EventHandler(TimerEventProcessor);
myAlarm.Set(2);
Console.WriteLine("Timer is set; alarm will go off in two seconds");
Application.Run();
}
//處理事件
private static void TimerEventProcessor(
Object myObject, EventArgs myEventArgs) {
if (MessageBox.Show("Wake up! Continue ringing?",
"Count is: " + alarmCounter,
MessageBox.YesNo) == DialogResult.Yes) {
alarmCounter += 1;
myAlarm.ReEnable();
}
else {
Application.Exit();
}
}
注意:Object和EventArgs不是必需的參數(shù),只是這是一個(gè)好的寫法模式而已
關(guān)于event還有好多沒有說(shuō),留著以后慢慢說(shuō)吧。