Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why should I check for null before I invoke the custom event?

Tags:

c#

events

What is the difference between these two code samples for invoking an event?

Sample 1

public void OnDataChanged()
{
    if (DataChanged != null)
    {
        DataChanged(this);
    }
}

Sample 2

DataChanged.Invoke(this);

When should I use each method to invoke a custom event? And why sometimes I get a NullReferenceException when I try to invoke event using DataChanged.Invoke(this), but when I convert the event invocation to the method in Sample 1 the DataChanged never becomes null anymore?

like image 551
a.toraby Avatar asked Oct 14 '15 10:10

a.toraby


People also ask

Why is my event handler null?

The event handler will be null unless somebody has subscribed to the event. As soon as a delegate is subscribed to the event, it will no longer be null. This protects you from a null exception if there has been no subscribers.

What is custom event in C#?

This is an easy way to create custom events and raise them. You create a delegate and an event in the class you are throwing from. Then subscribe to the event from another part of your code. You have already got a custom event argument class so you can build on that to make other event argument classes.

Can we use events without delegates?

Yes, you can declare an event without declaring a delegate by using Action. Action is in the System namespace.

Why do we need events in C#?

Events enable a class or object to notify other classes or objects when something of interest occurs. The class that sends (or raises) the event is called the publisher and the classes that receive (or handle) the event are called subscribers.


4 Answers

An OnXYZ method should always follow this form:

public void OnXYZ()
{
    var evt = XYZ;
    if (evt != null)
        evt(sender, e); // where to get e from differs
}

There are a couple of reasons for this form:

  1. The if evt != null check ensures that we don't try to invoke a null delegate. This can happen if nobody has hooked up an event handler to the event.
  2. In a multithreaded scenario, since delegates are immutable, once we've obtained a local copy of the delegate into evt, we can safely invoke it after checking for non-null, since nobody can alter it after the if but before the call.

What to pass for e differs, if you need to pass an EventArgs descendant with a parameter there are two ways:

public void OnXYZ(string p)
{
    var evt = XYZ;
    if (evt != null)
        evt(sender, new SomeEventArgs(p));
}

or more commonly this:

public void OnXYZ(SomeEventArgs e)
{
    var evt = XYZ;
    if (evt != null)
        evt(sender, e);
}

This syntax:

evt(sender, e);

is just a different way of writing this:

evt.Invoke(sender, e);

Also note that externally to your class, the event is an event, you can only add or remove event handlers from it.

Internal to your class, the event is a delegate, you can invoke it, check the target or method, walk the list of subscribers, etc.


Also, in C# 6 there is a new operator introduced, ?. - the Null-conditional operator - which is basically short for if not-null, dereference, which can shorten this method:

public void OnXYZ(SomeEventArgs e)
{
    var evt = XYZ;
    if (evt != null)
        evt(sender, e);
}

into this:

public void OnXYZ(SomeEventArgs e)
{
    XYZ?.Invoke(sender, e);
}

which can be further shortened with the use of Expression-bodied members:

public void OnXYZ(SomeEventArgs e) => XYZ?.Invoke(sender, e);

Please note that it is not possible to write this:

XYZ?.(sender, e);

so you must in this case use Invoke yourself.

like image 131
Lasse V. Karlsen Avatar answered Nov 15 '22 23:11

Lasse V. Karlsen


when I convert the event invocation to the method in Sample 1 the DataChanged never becomes Null

Then you're simply looking at two different scenarios.

if you don't declare an event like public event EventHandler YourEvent = delegate { };, then YourEvent is null until some consumer subscribes to it.

like image 33
CodeCaster Avatar answered Nov 15 '22 23:11

CodeCaster


If nothing has subscribed to DataChanged it will be set to null, and so when you attempt to do DataChanged.Invoke(this) you'll get a NullRefException as really it's trying to do null.Invoke(this). The reason for the additional if (DataChanged != null) is to avoid this occurring when nobody has subscribed to the event.

I don't believe that when you use Sample 1 DataChanged is never null, it's just never reaching the .Invoke to throw up the exception. It will always be null if nobody has subscribed.

like image 34
Callum Bradbury Avatar answered Nov 15 '22 23:11

Callum Bradbury


Are you sure, that in Sample 1 the DataChanged is never null? Or you just don't get the NullReference Exception (because you check if DataChanged is not null in the if statement)?

Let's start with the basics. Event is a special kind of delegate. When you call DataChanged(this) and DataChanged.Invoke(this), it's the same thing. Why? Because it compiles to the same thing. So all in all the DataChanged(this) is just a shorthand for calling DataChanged.Invoke(this).

Now, why do we need to check for null reference (like in sample 1). Basically, when you invoke an event, you invoke all methods that were subscribed to this event (e.g. DataChanged += someEventHandler). If nobody subscribed to this event, it will have a null value. No method was assigned to handle this event. In other words: the event handler is null.

That's why it's a good practice to check for null, before invoking an event.

like image 43
J. Swietek Avatar answered Nov 15 '22 22:11

J. Swietek