Rob
Rob

Reputation: 109

C# events, what's with that null thing?

I can't really understand what is this null testing thing for when raising an event.

Say i have this code.

    class ballClass
{


    public event EventHandler BallInPlay;


    public void onHit()
    {
        if (BallInPlay != null)
        {
            BallInPlay(this, new EventArgs());
        }
        else
        {
            MessageBox.Show("null!");
        }
    }



}

and I want to to raise a BallInPlay even when the onHit() method is triggered.

right now it shows me that the BallInPlay IS null. how or with what should i "fill" it for it to work?

Thanks!

Upvotes: 5

Views: 5515

Answers (7)

Jon Skeet
Jon Skeet

Reputation: 1499660

You need to subscribe to the event with a handler. For example:

BallClass ball = new BallClass();
ball.BallInPlay += BallInPlayHandler;
// Now when ball.OnHit is called for whatever reason, BallInPlayHandler
// will get called
...
private void BallInPlayHandler(Object sender, EventArgs e)
{
    // React to the event here
}

For more information, you may want to read my article on events and delegates.

Note that I've fixed the casing of BallClass and OnHit above - it's a good idea to use the standard .NET naming conventions to make your code fit in better with the code around it, and to make it more readable for others.

One thing to note: the nullity check you've got at the moment isn't thread-safe. The last subscriber could unsubscribe after the if but before the invocation. A thread-safe version would be:

public void OnHit()
{
    EventHandler handler = BallInPlay;
    if (handler != null)
    {
        handler(this, new EventArgs());
    }
    else
    {
        MessageBox.Show("null!");
    }
}

This wouldn't be guaranteed to use the latest subscribers (as there's no memory barrier involved) but it is guaranteed not to throw a NullReferenceException due to race conditions.

Upvotes: 7

Marlon
Marlon

Reputation: 20314

If there are no event handlers assigned to the event, the event is null. Think of an event like a List, when there are no items in the list the value of the event becomes null.

Attempting to invoke the event when there are no event handlers assigned to it will raise a NullReferenceException.

The null check prevents the NullReferenceException.

If you want BallInPlay to work you need to add an EventHandler.

Your code will be something like this:

BallInPlay += new EventHandler(YourFunctionNameGoesHere);

Upvotes: 2

BallInPlay(this, new EventArgs());

The reason for the null check before triggering the event will become obvious once you realise that the above line of code actually gets compiled as if you had written the following:

BallInPlay.Invoke(this, new EventArgs());
//        ^^^^^^^

And as we all know, it's a bad idea to call a method on a null reference. That's why you need to check that BallInPlay != null first.


Additional suggestions:

  • Use EventArgs.Empty instead of new EventArgs().

  • You could get around the check for null if you initialized BallInPlay as follows:

    public event EventHandler BallInPlay= delegate { };

    This works because now, there's always an "empty" handler method subscribed to this event. Thus you have a guarantee that the event delegate is no longer null after initialization. (Some people might consider this slightly inefficient.)

    You can always subscribe or unsubscribe event handlers with the += or -= operators on the BallInPlay event; for example:

    ball.BallInPlay += (sender, e) => { /* react to the triggered event */ };

  • I think your onHit (or, rather, OnHit if we're adhering to the usual conventions found in the .NET world) method should not be public, because this counters the whole purpose of events. Events are actually delegates, but with some added restrictions. One such restriction is that only the class defining the event can invoke ("trigger") it. Delegates, on the other hand, can be invoked by anyone. Thus, if you make onHit public, thereby allowing anyone to trigger your event, you could just as well have defined BallInPlay as a delegate, i.e. without the event keyword.

  • A thread-safe implementation of your onHit event trigger method is shown below. Note that a local copy of the event handler delegate is used inside the method. This is to make sure that the event handler won't be modified (e.g. by another thread) between the check for null and the invocation.


protected virtual void OnHit()
{
    EventHandler handler = BallInPlay;  // use a local copy of the event
    if (handler != null)                // for better thread-safety!
    {
        handler(this, EventArgs.Empty);
    }
}

Upvotes: 1

Øyvind Bråthen
Øyvind Bråthen

Reputation: 60684

The thing here is that if zero listeners are registered to listen to this event get raised, the event is null. If one or more listeners are registered it will have a value.

That means that if zero listeners are registered, and you try to raise the event without performing the null check, your program will throw an exception.

Upvotes: 10

TomTom
TomTom

Reputation: 62093

The null test is ther because if NOONE IS LISTENING to the event, the event object that you swuold call is actually null. So, without subscribers you would get a null pointer exception.

Upvotes: 2

Steve Greatrex
Steve Greatrex

Reputation: 15999

The null test is to determine whether or not anything is listening to the event. Once something attaches an event handler, BallInPlay will no longer be null

Upvotes: 1

Fredrik Widerberg
Fredrik Widerberg

Reputation: 3108

BallInPlay += new EventHandler(myFunc);

public void myFunc(object sender, EventArgs e)
{
MessageBox.Show("Woho \o/");
}

Upvotes: 0

Related Questions