前幾天和朋友老鄧討論delegate和event區(qū)別的時候,老鄧問我對他們的理解,當(dāng)時自己沒理解清楚,只是很簡單的一句話:event就是特殊的delegate,也即event是delegate的子集。并且我對老鄧解釋只要你愿意并完全信任調(diào)用自己代碼的client,你完全可以將所有的事件用delegate代替。
后面自己仔細(xì)思考了一下,發(fā)現(xiàn)自己理解的局限性,確實(shí)delegate和event有很多的相似之處,并且delegate完全能實(shí)現(xiàn)event的功能。但我并未認(rèn)證考慮為什么微軟要設(shè)計(jì)這樣一個限制了delegate功能的東西出來,也沒從觀察者角度理解events。相對delegate來說,客戶端即觀察者只能調(diào)用+=或者-+來添加自己對相應(yīng)事件觸發(fā)的通知,它不能調(diào)用new來實(shí)例化發(fā)布者的event事件如單擊事件,或者直接將發(fā)布者的event對象直接賦值null從而撤銷發(fā)布者所有通知列表,也不能通過調(diào)用諸如this.btn.clck(obj,e)之類的方式來觸發(fā)event發(fā)布事件通知。當(dāng)我們理解了觀察者模式并完全站在實(shí)際對象角度考慮相信就不難理解為什么event會比delegate多這么多限制了,很顯然,第一,觀察者不能也不應(yīng)該有權(quán)限實(shí)例化事件發(fā)布者的消息列表,同理,觀察者不應(yīng)該能控制事件發(fā)布者對事件的通知,這些所有的操作應(yīng)該都是發(fā)布者內(nèi)部的事件而不能交由外部對象來控制,因此,才產(chǎn)生了event對象,它是通過對delegate的限制來封裝一部分本來就不應(yīng)該暴露在外的行為,從而更符合面向?qū)ο蟮乃季S。我想,在發(fā)布者內(nèi)部的click應(yīng)該還是一個委托,不過在添加了event關(guān)鍵字之后,.net會通過一系列方法將這個delegate包裹起來從而封裝了一部分本來就不應(yīng)該暴露的行為。這樣更符合面向?qū)ο蟮淖龇ā?/span>
因此我認(rèn)為event在本質(zhì)上所做的工作應(yīng)該還是通過delegate來實(shí)現(xiàn)的,或者至少原理相同,event關(guān)鍵字只不過是clr給我們對所定義的delegate對象的一個封裝,這樣對象可以不必暴露本來就不應(yīng)該被外部對象看到的方法,如果愿意的話,我們完全可以自己去做這些封裝的工作。他們最終都是使得我們所定義的對象更加符合封裝的原則。
現(xiàn)在我們可以用代碼驗(yàn)證這個猜想是否正確,首先看一段代碼:
class Program
{
public static TestDelegate myDelegate;//普通的委托聲明
public static event TestDelegate myEvent;//事件聲明
static void Main(string[] args)
{
myDelegate += TestEvent;
myEvent += TestEvent;
myDelegate();
myEvent();
}
static void TestEvent()
{
Console.WriteLine("Hello Event");
}
}
代碼中事件除了多了個關(guān)鍵字聲明之外與普通委托并無不同,那么在Reflector中它們有何不同呢?
extends [mscorlib]System.Object
{
.event ConsoleApplication1.TestDelegate myEvent
{
.addon void ConsoleApplication1.Program::add_myEvent(class ConsoleApplication1.TestDelegate)
.removeon void ConsoleApplication1.Program::remove_myEvent(class ConsoleApplication1.TestDelegate)
}
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
{
}
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
}
.method private hidebysig static void TestEvent() cil managed
{
}
.field public static class ConsoleApplication1.TestDelegate myDelegate
.field private static class ConsoleApplication1.TestDelegate myEvent
}
這里我們可以看到在IL中它會首先聲明一個同名普通私有字段,注意這里該字段被聲明成私有的,這是為了防止外部對象非法訪問這個委托,然后再看上面的event會有兩個操作:
{
.addon void ConsoleApplication1.Program::add_myEvent(class ConsoleApplication1.TestDelegate)
.removeon void ConsoleApplication1.Program::remove_myEvent(class ConsoleApplication1.TestDelegate)
}
這個就是event關(guān)鍵字所做的封裝,也就是它允許myEvent委托僅僅暴露add和remove新的委托,而該委托的其他操作都被禁止了。
上面是我自己做的一些研究,后面我讀到CLR via C#時候看到對事件類似的描述,在書中第230頁(英文版)中提到, 在我們聲明一個事件的時候,其實(shí)編譯器會幫你生成一些代碼,e.g. public event EventHandler<NewMailEventArgs> NewMail;當(dāng)編譯器碰到這段代碼時,它會把它轉(zhuǎn)換成下面的代碼:
private EventHandler<NewMailEventArgs> NewMail = null;
// 2. A PUBLIC add_Xxx method (where xxx is the Event name)
// Allows objects to register interest in the event.
[MethodImpl(MethodImplOptions.Synchronized)]
public void add_NewMail(EventHandler<NewMailEventArgs> value) {
NewMail = (EventHandler<NewMailEventArgs>)
Delegate.Combine(NewMail, value);
}
// 3. A PUBLIC remove_Xxx method (where Xxx is the Event name)
// Allows objects to unregister interest in the event.
[MethodImpl(MethodImplOptions.Synchronized)]
public void remove_NewMail(EventHandler<NewMailEventArgs> value) {
NewMail = (EventHandler<NewMailEventArgs>)
Delegate.Remove(NewMail, value);
}
這段代碼跟我在Reflector中看到的IL類似。這里正是event對delegate所做的封裝了。
最后謝謝文楚,由于很少分享自己所得,很多東西我并未深入研究,僅僅停留在猜想階段就不了了。希望后面補(bǔ)充的東西能對別人有所幫助吧!
安徽新華電腦學(xué)校專業(yè)職業(yè)規(guī)劃師為你提供更多幫助【在線咨詢】