How to check for subscribers before raising an event in VB.NET
Asked Answered
B

2

8

In C#, you do something like this:

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

But what do you do in VB.NET?

There is RaiseEvent, but is

RaiseEvent Changed(Me, EventArgs.Empty)

actually checking that something has subscribed to the event?

Briny answered 23/5, 2013 at 10:15 Comment(1)
F
18

Unlike its C# equivalent, RaiseEvent in VB.NET will not raise an exception if there are no listeners, so it is not strictly necessary to perform the null check first.

But if you want to do so anyway, there is a syntax for it. You just need to add Event as a suffix to the end of the event's name. (If you don't do this, you'll get a compiler error.) For example:

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

Like I said above, though, I'm not really sure if there's any real benefit to doing this. It makes your code non-idiomatic and more difficult to read. The trick I present here isn't particularly well-documented, presumably because the whole point of the RaiseEvent keyword is to handle all of this for you in the background. It's much more convenient and intuitive that way, two design goals of the VB.NET language.

Another reason you should probably leave this to be handled automatically is because it's somewhat difficult to get it right when doing it the C# way. The example snippet you've shown actually contains a race condition, a bug waiting to happen. More specifically, it's not thread-safe. You're supposed to create a temporary variable that captures the current set of event handlers, then do the null check. Something like this:

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

Eric Lippert has a great blog article about this here, inspired by this Stack Overflow question.

Interestingly, if you examine disassembled VB.NET code that utilizes the RaiseEvent keyword, you'll find that it is doing exactly what the above correct C# code does: declares a temporary variable, performs a null check, and then invokes the delegate. Why clutter up your code with all this each time?

Featherstone answered 23/5, 2013 at 11:30 Comment(3)
Yeah, know about the thread safety. But for the code I write unless I know my code will be used with threads I go for the framework approach : only my static methods are guaranteed to be thread safe.Briny
@user That's risky. You should always strive to write thread-safe code whenever possible, that way when you decide to go back later and add additional threads, you don't have to go through and rewrite everything. If I write something that is not thread-safe and I don't intend for it to be, I comment it well with such an explanation. Saves a lot of pain later.Featherstone
This is helpful if you want to require that at least somebody is listening and raise the error to know some plumbing has not gotten connected right.Destitution
P
2

A direct conversion would be:

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

However RaiseEvent has to be used to raise events in VB.NET, you cannot simply call the event. No exception is thrown using RaiseEvent if there are no listeners.

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

Therefore the following should be used:

RaiseEvent Changed(Me, EventArgs.Empty)
Purree answered 23/5, 2013 at 10:18 Comment(1)
I saw this article but it doesn't say what happens if there are no listeners.Briny

© 2022 - 2024 — McMap. All rights reserved.