为什么我可以检查一些事件处理程序为null,有些不是吗?

时间:2010-12-21 16:10:24

标签: c# .net winforms

我有一段丑陋的代码添加了事件处理程序。问题是,如果多次调用代码,则会多次调用事件处理程序。

要解决此问题,我先删除事件处理程序然后添加它。

现在我已经看到了以下行为:

可以像以下一样检查某些事件处理程序:

if (object.event == null) {
    //
    // Code
    //
}

其他形式的

if (object.object.event == null) {
    //
    // Code
    //
}

我收到了一条消息,例如' object.object.event'可能只发生在 - =或+ =的左边。 (由于我使用德语版的visual studio,我不知道正确的英语翻译。)

我不知道为什么这种行为看起来不合适所以我会很感激这方面的一些信息。

更具体地说:它是用户控制的。

if (myControl.Event == null) {
    //
    // works
    //
}

if (myControl.TreeView.NodeMouseClick == null) {
    //
    // doesn't work
    //
}

5 个答案:

答案 0 :(得分:4)

  

要解决此问题,我先删除事件处理程序然后添加它。

这不能解决问题。 event 关键字为委托对象提供访问器。就像一个属性为一个领域提供访问者。在属性上,您始终需要一个 get set 。事件具有添加,删除和提升访问者。但是如果您自己不这样做,编译器将为它们生成默认实现。这很常见。

属性访问器的优点是支持字段可以是私有的。除了包含该字段的类之外,没有人可以搞乱它。所有访问都必须通过get和set访问器。 event 关键字的工作方式完全相同,除了包含该事件的类中的代码之外,没有人可以使用委托对象。

在你试图避免引发事件的过程中,这是一个巨大的漏洞。你不能搞乱在另一个类中声明的事件的订阅者列表,编译器告诉你这一点。这样做的正常方法是设置bool标志以指示必须暂时忽略事件。事件处理程序可以检查该标志并避免执行任何副作用。

答案 1 :(得分:3)

您只能访问班级中定义的活动的支持字段 有关更多信息,请参阅spec。 (虽然C#4中的this has changed,但更改与您无关)

您的最佳做法是在每个班级中创建protected internal OnEventName方法。

答案 2 :(得分:3)

SLaks是正确的,并与一些优秀的资源相关联。以下是Chris Burrows blog article的相关引用:

  

让我快速绕道而行,向您解释+ =的绑定如何在C#中起作用。有两种可能性:

     
      
  1. 要么存在实际的+运算符,例如使用整数,并且x + = y绑定到“x = x + y”,除了x仅被计算一次。这是复合赋值运算符;或
  2.   
  3. 左侧的东西是一个事件,x.E + = y绑定到“x.add_E(y)”。这是事件访问器操作符,实际上这是绑定到事件访问器的唯一方法。
  4.         

    那么我们在上面的代码段中有什么?好吧,您需要确定的额外细节是关于C#中类似字段事件的以下规则:在类或结构之外定义类似字段的事件E,绑定到名称E解析为事件本身,唯一合法的操作是调用访问者;在定义类似字段的事件E的类或结构内部,绑定到名称E解析为私有委托字段

在您的情况下,在解析myControl.Event时,您在<{>} myControl类中,因此您看不到事件对象;相反,您会看到一个实际的委托对象,您可以将其与null进行比较。在解析myControl.TreeView.NodeMouseClick时,您在外部 TreeView类,因此您无法访问实际的委托对象;你得到的只是事件对象,无法与null比较。

如果我理解正确的话,所有这些对你来说都无济于事,因为大概是在你检查了null之后,你会尝试为它发起TreeView的事件,你可以'做。

根据您要执行的操作,您可能会继承TreeView并添加internal方法,该方法将调用受保护的TreeView.OnNodeMouseClick方法来触发事件。

答案 3 :(得分:0)

您只能查询自己的事件处理程序以获取附加的侦听器。

答案 4 :(得分:0)

自动事件,如下所示:

public event EventHandler SomethingHappened;

由编译器使用多播委托实现。

编写myControl.Event == null时,编译器实际上需要在该委托上调用Delegate.GetInvocationList。编译器不允许你这样做,除非代码在暴露事件的类的方法内,因此错误(它只允许你在调用列表中添加或删除)。

如果我们在讨论您在自己的类中定义的事件,那么您可以选择公开调用列表(例如通过方法)并执行您要执行的操作。但是对于现有的类(例如TreeView),这是不可能的。