如何在VB.NET中在引发事件之前检查订阅者

8

在C#中,你可以这样做:

if (Changed != null)
    Changed(this, EventArgs.Empty);

你在VB.NET中做什么?

RaiseEvent,但是它是什么呢?

RaiseEvent Changed(Me, EventArgs.Empty)

实际上检查是否有内容订阅了事件?


1
http://msdn.microsoft.com/en-us/library/fwd3bwed%28v=vs.110%29.aspx - VikciaR
2个回答

18

与其C#等效项不同,VB.NET中的RaiseEvent在没有监听器时不会引发异常,因此不一定需要先执行空值检查。

但是,如果您仍然想这样做,有一个语法可以实现。您只需要在事件名称末尾添加Event作为后缀即可(如果不这样做,则会出现编译器错误)。例如:

If ChangedEvent IsNot Nothing Then
    RaiseEvent Changed(Me, EventArgs.Empty)
End If

就像我上面说的那样,我不确定这样做是否有实际好处。它会使你的代码非惯用语并且更难阅读。我在这里介绍的技巧没有被很好地记录下来,可能是因为 RaiseEvent 关键字的整个目的就是在后台处理所有这些内容,这样更加方便和直观,这是 VB.NET 语言的两个设计目标。

另一个你应该让系统自动处理的原因是当按照 C# 的方式进行操作时会比较困难。你展示的示例片段实际上包含一种竞态条件,等待发生错误的 bug。具体来说,它不是线程安全的。你应该创建一个临时变量来捕获当前的事件处理程序,然后再进行空值检查。像这样:

EventHandler tmp = Changed;
if (tmp != null)
{
    tmp(this, EventArgs.Empty);
}

Eric Lippert在他的博客文章中详细讨论了此问题,文章链接在这里,受到这个Stack Overflow问题的启发。

有趣的是,如果你检查使用RaiseEvent关键字的VB.NET代码的反汇编结果,你会发现它正在执行与上面C#代码所做的正确事情一样:声明一个临时变量,进行空值检查,然后调用委托。为什么要每次都让你的代码变得混乱呢?


是的,我知道线程安全的问题。但是对于我编写的代码,除非我知道我的代码将与线程一起使用,否则我会采用框架方法:只有我的静态方法可以保证线程安全。 - Sacha K
@user 那很冒险。尽可能地编写线程安全的代码是你应该始终努力的方向,这样当你决定稍后添加其他线程时,就不必重新编写所有内容了。如果我编写的某些内容不是线程安全的,并且我并不打算让它成为线程安全的,我会用适当的注释进行解释。这样可以避免以后出现很多麻烦。 - Cody Gray
如果您想要确保至少有人在监听并且希望通过引发错误来了解某些管道连接不正确,那么这将非常有帮助。 - dblwizard

2
直接转换的方法是:
If Changed IsNot Nothing Then
    Changed(Me, EventArgs.Empty)
End If

然而,在VB.NET中,必须使用RaiseEvent来触发事件,不能直接调用事件。如果没有侦听器,则使用RaiseEvent不会抛出异常。

参见:http://msdn.microsoft.com/en-GB/library/fwd3bwed(v=vs.71).aspx

因此,应该使用以下内容:

RaiseEvent Changed(Me, EventArgs.Empty)

我看到了这篇文章,但它没有说明如果没有听众会发生什么。 - Sacha K

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