在C#中调用事件处理程序

10

我一直在尝试学习如何在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)吗?


相关链接:https://dev59.com/8XE95IYBdhLWcg3wHqMV - H H
7个回答

15

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

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

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

handler?.Invoke(this, e);
handler(this, e) 会调用所有已注册的事件监听器。事件监听器使用 += 运算符订阅事件,使用 -= 运算符取消订阅该事件。 this 是为了让事件监听器知道是谁触发了 ThresholdReached 事件。即事件的发送者。 e 是事件参数,也传递到监听器方法中,其中可以包含更多关于 ThresholdReached 事件的有用信息,例如达到了哪个阈值。

谢谢您的解释。您如何注册事件监听器呢? - Tony
使用 += 运算符。就像在你的其他类中一样,yourInstanceWithTheEvent.ThresholdReached += SomeThresholdReachedHandler; - BlueM

1

它正在引发一个带有参数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参数调用函数。


2
@daryal:想象一下,如果在“! = null”检查之后最后一个侦听器取消订阅会发生什么。 - BlueM
@Oded,"handler"和"ThresholdReached"不是指向同一引用吗?如果我错过了什么,请原谅。 - daryal
@daryal - 不是的。调用列表被复制到handler引用中。请参阅Eric Lippert在此主题上的博客文章 - Oded
也许理解事件是一个实例变量,它持有对MultiCastDelegate的引用会有所帮助。如果最后一个侦听器取消订阅,则会删除该多播委托实例并将事件设置为null。http://msdn.microsoft.com/de-de/library/system.multicastdelegate.aspx - BlueM
抱歉,我之前误解了事件是多播委托实例。但请阅读http://csharpindepth.com/Articles/Chapter2/Events.aspx中的“线程安全事件”以获取更多信息。 - BlueM

1

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

之所以会得到当前调用列表的快照,是因为委托是不可变的。您获得了对当前多路广播委托的引用,当添加或删除处理程序时,后备字段指向从两个不可变委托创建的新委托。

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


0
它是在尝试使用事件(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 会创建处理程序的副本,这在 这个问题中有更好的解释。


0

对 handler 的调用表示在另一个对象或类中的函数调用。当您创建该对象时,您将能够编写类似于以下代码的代码片段:

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)订阅者的调用。


0

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


哦,那样更有意义。谢谢。 - Tony

0

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


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