为什么具有自定义访问器的事件不能像默认事件那样直接触发?

7
如果我写下如下代码:
class MainClass
{
    static EventHandler _myEvent= delegate{};
    static event EventHandler MyEvent
    {
        add{_myEvent += value;}
        remove{_myEvent -= value;}
    }

    public static void Main (string[] args)
    {
        MyEvent(null,EventArgs.Empty);
    }
}

编译器会报错:错误 CS0079: 事件 MainClass.MyEvent' 只能出现在 '+=' 或 '-=' 操作符的左侧。

为什么会有这样奇怪的限制?如果我不能直接触发一个事件,为什么我要在第一时间使用它呢?这是一个bug吗(我在使用mono)还是故意设计的微妙之处?请问有人可以教我背后的理由吗?提前感谢。

2个回答

3
你只能在声明类中访问事件。在幕后,.NET创建私有实例变量来保存委托。
编译器实际上创建了一个公共事件和一个私有字段。您可以直接从同一类或嵌套类访问该字段。从外部类,您只能订阅/取消订阅。
Jon Skeet给出了很好的信息,详细介绍了为什么会这样以及它是如何工作的,可以在此处(http://csharpindepth.com/Articles/Chapter2/Events.aspx)阅读到。

这不是问题所在。尝试访问事件_is_来自声明类内部... - Nick Guerrera
@Nick:不,这绝对说明了发生了什么情况:对于类似字段的事件,看起来是调用事件实际上是通过字段进行调用。然后,你可以利用这个知识来应用于自定义事件 - 也通过后备字段来调用它。 - Jon Skeet
@Jon:我同意你可以从实现类似字段的事件开始逆向工作,以理解错误CS0079。如果答案集中在这一点上,那么我会给予+1并结束。然而,答案侧重于字段的私有可访问性,这是无关紧要的。 - Nick Guerrera
@Jon:这是每个句子的一部分,除了最后一个,但是嘿... ;) - Nick Guerrera

2

您需要调用手动声明的后备委托来触发事件:将 MyEvent(null, EventArgs.Empty) 替换为 _myEvent(null, EventArgs.Empty)。如果您仔细想想,自定义的 add/remove 可以在任何地方存储委托,这就是为什么您不能按照您编写的方式检索和调用它们的原因...


但是,如果我想派生类而不想将底层委托字段暴露给子类怎么办?那会很混乱,不是吗?我刚刚观察到子类不允许触发其基类的事件。这个设计不是有点弱吗?事件被用来防止外部人员搞砸它。但为什么内部人员也受到限制呢? - Need4Steed
推荐的模式是提供一个受保护的虚拟方法来引发事件:http://msdn.microsoft.com/en-us/library/ms229011.aspx。 - Nick Guerrera
1
谢谢你的链接。然而,我仍然认为不允许触发设计相当糟糕。事件可以是虚拟的,并且可以被子类重写,但是要触发它们,我们必须诉诸于一些辅助虚拟方法,这怎么不糟糕呢? - Need4Steed

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接