如果事件在.NET中作为委托实现,那么.event IL部分的重点是什么?

我在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 } 

由于事件只不过是使用编译器生成的addremove方法的委托,因此我没想到remove事件视为IL中的事件。 但是添加和删除方法是在一个以.event开头的部分中定义的,而不是像普通方法那样的.method

我的最终问题是:如果事件只是作为具有访问器方法的.event实现,那么拥有.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); } 

(以上几乎是胜利forms事件的支柱)

Facade(虽然这会使“发件人”混淆一些;一些中间代码通常很有帮助):

 private Bar wrappedObject; // via ctor public event EventHandler SomeEvent { add { wrappedObject.SomeOtherEvent += value; } remove { wrappedObject.SomeOtherEvent -= value; } } 

(以上也可用于有效地重命名事件)

事件与代表不同。 事件封装添加/删除事件的处理程序。 处理程序用委托表示。

可以为每个事件编写AddClickHandler / RemoveClickHandler等 – 但这会相对痛苦,并且不会让像VS这样的工具轻松地将事件与其他事件分开。

这就像属性一样 – 您可以编写GetSize / SetSize等(就像在Java中一样),但通过分离属性,可以使用语法快捷方式和更好的工具支持。

拥有一对add,remove方法的事件就是封装

大多数时间事件都按原样使用,但有时您不希望在事件中存储附加到事件的委托,或者您希望在添加或删除事件方法时执行额外处理。

例如,实现内存高效事件的一种方法是将委托存储在字典而不是私有字段中,因为字段总是被分配,而字典只在添加项目时增大。 此模型类似于Winforms和WPF使用的内容使得有效使用内存(winforms和WPF使用键控字典来存储代理而不是列表)