编译器生成的事件的后备字段是否始终保证使用与事件相同的名称?

C#允许我们创建自定义事件访问器 。

Action _custom; public event Action Custom { add { _custom = (Action)Delegate.Combine( _custom, value ); } remove { _custom = (Action)Delegate.Remove( _custom, value ); } } 

如果未指定它们, 编译器将为您创建它们 。 C#语言规范:

编译类似字段的事件时,编译器会自动创建存储以保存委托,并为事件创建访问器,以便向委托字段添加或删除事件处理程序。

反编译的源代码使用dotPeek进行简单的public event Action Public; 看起来如下:

  private Action Public; public event Action Public { add { Action action = this.Public; Action comparand; do { comparand = action; action = Interlocked.CompareExchange( ref this.Public, comparand + value, comparand); } while (action != comparand); } remove { Action action = this.Public; Action comparand; do { comparand = action; action = Interlocked.CompareExchange( ref this.Public, comparand - value, comparand); } while (action != comparand); } } 

值得注意的是, 该字段和事件使用相同的名称 。 这导致一些人得出结论,通过在类中查找与事件同名的字段,您可以在reflection期间找到有关支持字段的信息。 我实现如下:

 public static FieldInfo GetFieldInfo( this EventInfo eventInfo ) { Contract.Requires( eventInfo != null ); return eventInfo.DeclaringType.GetField( eventInfo.Name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic ); } 

这有效,但提出了一个问题: 编译器生成事件的支持字段是否始终保证使用与事件相同的名称?

无法使用Visual Studio创建使用相同名称访问委托的自定义事件访问器。 这会产生以下消息: “已声明具有相同名称的成员”。 我想知道你是否可以得出结论,任何没有相同名称的支持代表可用的事件都是具有自定义访问者的事件。

编译器生成的事件的后备字段是否始终保证使用与事件相同的名称?

乔恩和马克回答“不”是完全正确的。

这是编译器的未记录的实现细节,由规范明确指出,并且可能随时更改。

在实践中,这不太可能改变。 我们使用这样的事实:字段和事件具有相同的名称,这是在编译器中将它们在逻辑上相互关联的最简单方法。

无法使用Visual Studio创建使用相同名称访问委托的自定义事件访问器。 这会产生以下消息:“已声明具有相同名称的成员”。

正确。

我想知道你是否可以得出结论,任何没有相同名称的支持代表可用的事件都是具有自定义访问者的事件。

我不太愿意做出这样的结论。 如果您知道有问题的程序集是从C#编译器发出的,那么您可能会得出这个结论。 但是,在发射assembly时,我们并不是城里唯一的游戏。 你可以用ILDASM做一些非常奇怪的事情。

我可以问你为什么要知道这些东西吗? 我同意马克; 如果你通过Reflection访问一个字段,你可能做错了。 您应该能够访问类中的字段没有问题(因为它只是一个私有字段),并且从类外部,您没有业务查看另一个类的私有实现细节。 使用reflection来围绕由访问器强加的线程安全性进行最终运行是特别令人震惊的。 那些存取器可供您保护; 不要乱跑他们。

否 – 来自C#4规范(第10.8.1节):

在类X中,对Ev的引用被编译为引用隐藏字段_ Ev。 名称“ _Ev”是任意的; 隐藏字段可以有任何名称或根本没有名称。

因此,虽然保证了源代码兼容性,但无法保证生成的字段的名称。 (在实践中,我不希望在MS编译器中很快就会改变它 – 但它不能保证,所以你不应该做出假设。)

事件的实现是编译器实现细节,并且在编译器之间不同(MS c#4具有与MS c#<4不同的实现,甚至MS和ECMA规范也不同意)。

就个人而言,我会说:如果你需要通过reflection访问支持字段,你可能没有正确使用事件。