我在Stack Overflow上看到了一些关于委托,事件和这两个特性的.NET实现的非常好的问题.特别是一个问题," C#Events如何在幕后工作? ",产生了一个很好的答案,很好地解释了一些微妙的观点.
上述问题的答案提出了这一点:
当您声明类似字段的事件时......编译器会生成方法和私有字段(与委托类型相同).在类中,当您引用ElementAddedEvent时,您指的是该字段.在课外,你指的是这个领域
从同一问题("类字段事件 ")链接的MSDN文章添加:
引发事件的概念恰好等同于调用事件所代表的委托 - 因此,没有用于引发事件的特殊语言结构.
为了进一步检查,我构建了一个测试项目,以便查看IL被编译为一个事件和一个委托:
public class TestClass { public EventHandler handler; public event EventHandler FooEvent; public TestClass() { } }
我希望委托字段handler
和事件FooEvent
编译成大致相同的IL代码,并使用一些其他方法来封装对编译器生成的FooEvent
字段的访问.但是IL产生的不是我预期的:
.class public auto ansi beforefieldinit TestClass extends [mscorlib]System.Object { .event [mscorlib]System.EventHandler FooEvent { .addon instance void TestClass::add_FooEvent(class [mscorlib]System.EventHandler) .removeon instance void TestClass::remove_FooEvent(class [mscorlib]System.EventHandler) } .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Constructor IL hidden } .field private class [mscorlib]System.EventHandler FooEvent .field public class [mscorlib]System.EventHandler handler }
由于事件只不过是使用编译器生成add
和remove
方法的委托,因此我没想到会将事件视为IL中的事件.但是添加和删除方法是在一个开始的部分中定义的.event
,而不是.method
正常的方法.
我的最终问题是:如果事件只是作为具有访问方法的代表实现,那么拥有.event
IL部分有什么意义?如果不通过使用.method
部分,它们不能在IL中实现吗?是.event
相当于.method
?
我不确定这是否令人惊讶......与属性vs字段相比(因为属性与事件相同:通过访问器封装):
.field public string Foo // public field .property instance string Bar // public property { .get instance string MyType::get_Bar() .set instance void MyType::set_Bar(string) }
此外 - 事件没有提到任何关于领域的事情; 他们只定义访问者(添加/删除).代表支持者是一个实现细节; 事实上,类似字段的事件将字段声明为支持成员 - 就像auto-implemented-properties将字段声明为支持成员一样.其他实现是可能的(并且非常常见,尤其是在表单等中).
其他常见实现:
稀疏事件(控件等) - EventHandlerList(或类似):
// only one instance field no matter how many events; // very useful if we expect most events to be unsubscribed private EventHandlerList events = new EventHandlerList(); protected EventHandlerList Events { get { return events; } // usually lazy } // this code repeated per event private static readonly object FooEvent = new object(); public event EventHandler Foo { add { Events.AddHandler(FooEvent, value); } remove { Events.RemoveHandler(FooEvent, value); } } protected virtual void OnFoo() { EventHandler handler = Events[FooEvent] as EventHandler; if (handler != null) handler(this, EventArgs.Empty); }
(以上几乎是胜利形式事件的支柱)
Facade(虽然这会使"发件人"混淆一些;一些中间代码通常很有帮助):
private Bar wrappedObject; // via ctor public event EventHandler SomeEvent { add { wrappedObject.SomeOtherEvent += value; } remove { wrappedObject.SomeOtherEvent -= value; } }
(以上也可用于有效地重命名事件)