简体   繁体   中英

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

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?

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.

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.

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.

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 (eg 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.

An Example:

public void OnAbc(){
    var data=Abc;

    if(!String.IsNullOrEmpty(data))
        Abc(sender,e);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM