C#中的事件是有趣的事情。它們非常像自動屬性,但具有私有get方法和公共方法(或任何您選擇的訪問方式)設置方法。
請允許我演示。讓我們用假設的事件創建一個假設的類。
class SomeObject{
public event EventHandler SomeEvent;
public void DoSomeStuff(){
OnSomeEvent(EventArgs.Empty);
)
protected virtual void OnSomeEvent(EventArgs e){
var handler = SomeEvent;
if(handler != null)
handler(this, e);
}
}
該類遵循暴露事件的類的典型模式。它公開地公開事件,但具有受保護的虛擬「On ...」方法,默認情況下,該方法只是簡單地調用事件(如果它有任何子類)。這種受保護的虛擬方法不僅封裝的實際調用該事件的邏輯,但對於派生類提供了一種方法:
- 方便地與較小的開銷處理該事件,
- 之前或所有外部用戶後進行一些處理接收該事件,
- 呼叫一個完全不同的事件,或
- 完全抑制該事件。
但是,什麼是所謂的SomeEvent這個 「事件」 的對象?在C#中,我們熟悉字段,屬性和方法,但究竟是一個事件?
在我們進入的是,它有助於認識到,有真的只有兩個C#類型的類成員:字段和方法。屬性和事件或多或少只是語法上的糖。
屬性是真正的一個或兩個方法,並存儲在元數據的名稱,在C#編譯器允許你使用指這兩種方法中的一種。也就是說,當你像這樣定義一個屬性:
public string SomeProperty{
get{return "I like pie!";}
set{
if(string.Compare(value, "pie", StringComparison.OrdinalIgnoreCase) == 0)
Console.WriteLine("Pie is yummy!");
else Console.WriteLine("\"{0}\" isn't pie!", value ?? "<null>");
}
}
編譯器寫入兩種方法給你:
public string get_SomeProperty(){return "I like pie!";}
public void set_SomeProperty(string value){
if(string.Compare(value, "pie", StringComparison.OrdinalIgnoreCase) == 0)
Console.WriteLine("Pie is yummy!");
else Console.WriteLine("\"{0}\" isn't pie!", value ?? "<null>");
}
我不是這個意思傾斜。這兩個方法實際上成爲您的編譯類的一部分以及關於屬性的大量元數據,這些元數據會在下次從編譯器讀取(get)或寫入(set)時通知編譯器調用哪些方法。所以,當你寫這樣的代碼:
var foo = someObject.SomeProperty;
someObject.SomeProperty = foo;
編譯器發現分配給SomeProperty
getter和setter方法,並把你的代碼到:
string foo = someObject.get_SomeProperty();
someObject.set_SomeProperty(foo);
這就是爲什麼如果你定義一個類有公共領域,但後來決定將其更改爲屬性,以便在從中讀取或寫入時可以執行一些有趣的事情,您必須重新編譯包含對此成員的引用的任何外部程序集,因爲什麼是字段訪問指令需要而是成爲方法調用指令。
現在該物業是有點反常,因爲它不依賴於任何支持字段。它的getter返回一個常量值,其setter沒有在任何地方存儲它的值。需要明確的是,這是完全合法的,但大多數時候,我們定義更多像這樣的屬性:
string someProperty;
public string SomeProperty{get{return someProperty;}set{someProperty = value;}}
此屬性不會做的比讀寫字段的任何其他。這幾乎是相同的命名SomeProperty
公共領域,除非你可以在日後添加邏輯到getter和setter,未做類重新編譯的消費者。但這種模式是如此普遍,即C#3添加的「自動屬性」來達到同樣的效果:
public string SomeProperty{get;set;}
編譯器開啓這個進相同的代碼如我們上面寫的,不同之處在於支持字段具有超級祕密只有編譯器知道的名稱,所以我們只能引用我們代碼中的屬性,即使在類本身中也是如此。
因爲後備字段是無法進入美國,而你可能只讀屬性是這樣的:
string someProperty;
public string SomeProperty{get{return someProperty;}}
你幾乎從來沒有看到只讀自動屬性(編譯器讓你寫他們,但你會發現他們很少使用):
public string SomeProperty{get;} // legal, but not very useful unless you always want SomeProperty to be null
相反,你通常會看到是這樣的:
public string SomeProperty{get;private set;}
連接到set
的private
訪問修飾符能夠爲類中的方法來設置屬性,但屬性仍然出現只讀外的類。
「現在是什麼任何這與事件怎麼辦?」你可能會問。那麼,事實上,事件就像一個自動屬性。通常,當你聲明一個事件時,編譯器會生成一個超級祕密的後臺字段和一對方法。除了後臺字段不是超級祕密,並且這對方法不是「get」和「set」,它們是「add」和「remove」。讓我來證明一下。
當你寫這樣的一個事件:
public event EventHandler SomeEvent;
什麼編譯器寫的是:
EventHandler SomeEvent;
public void add_SomeEvent(EventHandler value){
SomeEvent = (EventHandler)Delegate.Combine(SomeEvent, value);
}
public void remove_SomeEvent(EventHandler value){
SomeEvent = (EventHandler)Delegate.Remove(SomeEvent, value);
}
它還增加了一些元數據膠水這樣以後,當你寫這樣的代碼:
void Awe_SomeEventHandler(object sender, EventArgs e){}
void SomeMethod(SomeObject Awe){
Awe.SomeEvent += Awe_SomeEventHandler
Awe.SomeEvent -= Awe_SomeEventHandler
}
編譯器將其重寫爲(僅有趣的行):
Awe.add_SomeEvent(Awe_SomeEventHandler);
Awe.remove_SomeEvent(Awe_SomeEventHandler);
採取這裏開始,真正重要的是,有關SomeEvent
唯一公開訪問的成員是那些添加和刪除的方法,當你使用+=
和-=
運營商的稱。支持字段,即保存事件訂閱者的名爲SomeEvent的委託對象是隻有聲明類的成員才能訪問的私有字段。
然而,很像方式自動屬性僅適用於手寫支持字段和getter和setter一個快捷方式,可以顯式聲明的委託,並添加和刪除方法以及:
internal EventHandler someEvent;
public event EventHandler SomeEvent{
add{someEvent = (EventHandler)Delegate.Combine(someEvent, value);}
remove{someEvent = (EventHandler)Delegate.Remove(someEvent, value);}
}
然後,您的裝配中的其它類可以觸發你的事件:
var handler = Awe.someEvent;
if(handler != null)
handler(Awe, EventArgs.Empty);
但是,它更容易和更地道來定義您的活動正常(自動)的方式,只是揭露「提高」的方法:
internal void RaiseSomeEvent(){OnSomeEvent(EventArgs.Empty);}
但是現在你希望瞭解爲什麼你必須這樣做,以及後臺發生了什麼。