【问题标题】:Calling an event handler in C#在 C# 中调用事件处理程序
【发布时间】:2012-08-31 14:40:21
【问题描述】:

我一直在尝试学习如何在 C# 中使用事件处理程序,但我无法弄清楚 handler(this, e) 在以下代码中的作用:

public event EventHandler ThresholdReached;

protected virtual void OnThresholdReached(EventArgs e)
{
    EventHandler handler = ThresholdReached;
    if (handler != null)
    {
        handler(this, e);
    }
}

它是否试图用事件(e)调用事件处理方法(this)?

【问题讨论】:

标签: c# events


【解决方案1】:

它调用在ThresholdReached 事件上注册的所有已注册事件侦听器。

handler != null 检查确保至少有一个侦听器注册到该事件。

在 C# 6.0 及更高版本中,您可以使用Null Propagation

handler?.Invoke(this, e);

handler(this, e) 将调用每个注册的事件监听器。事件侦听器在 += 运算符的帮助下订阅该事件,并通过 -= 运算符取消订阅该事件。

this 是为了让事件侦听器知道谁引发了 ThresholdReached 事件。谁是事件的发送者。

e 是事件参数,它也被传递到侦听器方法中,该方法可以包含有关 ThresholdReached 事件的更多有用信息,例如达到了哪个阈值。

【讨论】:

  • 感谢您的解释。但是,您如何注册该事件的侦听器?
  • 使用 += 运算符。就像在您的其他课程中一样 yourInstanceWithTheEvent.ThresholdReached += SomeThresholdReachedHandler;
【解决方案2】:

它使用参数 sender=this 和 eventarguments = e 引发 ThresholdReached 事件。 其实和下面是一样的;

public event EventHandler ThresholdReached;

protected virtual void OnThresholdReached(EventArgs e)
{
    if (ThresholdReached != null)
    {
        ThresholdReached(this, e);
    }
}

如果该事件有监听者;它只会调用侦听器委托;

this.ThresholdReached += new EventHandler(Form1_ThresholdReached);

然后,当引发此事件时,Form1_ThresholdReached 函数将使用thise 参数调用。

【讨论】:

  • @daryal:想象一下如果最后一个侦听器在 != null 检查后取消订阅会发生什么。
  • @Oded 的“handler”和“ThresholdReached”不是指向同一个引用吗?对不起,如果我遗漏了一些观点。
  • @daryal - 否。调用列表复制handler 引用。请参阅 Eric Lippert 的 this blog post 关于该主题。
  • 也许它有助于理解事件是一个实例变量,它包含对 MultiCastDelegate msdn.microsoft.com/de-de/library/system.multicastdelegate.aspx 的引用如果最后一个侦听器取消订阅,它将删除该 multicastdelegate 实例并将事件设置为 null。
  • 对不起,我误认为事件是多播委托实例。但请阅读csharpindepth.com/Articles/Chapter2/Events.aspx“线程安全事件”了解更多信息。
【解决方案3】:

您示例中的代码将所有已注册的处理程序复制到局部变量handler,检查调用列表是否为空,并使用参数thise 调用复制的调用列表的所有成员。

您获得当前调用列表的快照的原因是delegates are immutable。您会获得对当前多播委托的引用,并且在添加或删除处理程序时,支持字段指向从两个不可变的委托创建的新委托。

将调用列表复制到局部变量的通常原因是某种形式的线程安全:处理程序可以在通常的空值检查(检查调用列表不为空)和实际调用之间取消订阅:这样您可能会意外触发没有处理程序的事件,并且会抛出 NullReferenceException

【讨论】:

    【解决方案4】:

    handler 指的是您的 ThresholdReached 事件。因此,如果有人订阅了ThresholdReached 事件,他们注册的处理程序将使用参数thise 调用。

    【讨论】:

    • 哦,这更有意义。谢谢。
    【解决方案5】:

    它正在触发ThresholdReached 事件。传递对自身的引用,this。在e 中传递有关事件的参数。

    【讨论】:

      【解决方案6】:

      它是否试图用事件(e)调用事件处理方法(this)?

      不,不是字面意思。它使用EventArgs e 调用事件处理程序,并使用this 作为发送者。也可能是:

      if (ThresholdReached != null)
      {
          ThresholdReached(this, e);
      }
      

      或者,绕过空检查:

      public event EventHandler ThresholdReached = delegate { };
      
      protected virtual void OnThresholdReached(EventArgs e)
      {
          ThresholdReached(this, e);
      }
      

      但是,正如@Oded 所说,第一部分不是线程安全的,因为EventHandler handler = ThresholdReached 创建了处理程序的副本,this question 对此进行了更好的解释。

      【讨论】:

        【解决方案7】:

        对处理程序的调用表示另一个对象或类中的函数调用。当您创建对象时,您将能够编写如下所示的一段代码:

        obj.ThreasholdReached += new EventHandler(someFunction);
        

        该类中的someFunction 将像这样定义

        public someFunction(object sender, EventArgs e) {...}
        

        原始对象中的OnThreasholdReached 函数将事件发布到已将函数分配给ThreasholdReached 处理程序的任何其他类。使用handler 作为中间人是完全不必要的额外步骤。你还在说if ThreasholdReached != null,还是一样的。

        总结:代码行 handler(this, e) 实际上是对已分配给对象的 ThreasholdReached 事件的任何订阅者 someFunction(object sender, EventArgs e) 的调用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多