Reputation: 3391
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?
Upvotes: 7
Views: 4332
Reputation: 1
An Example:
public void OnAbc(){
var data=Abc;
if(!String.IsNullOrEmpty(data))
Abc(sender,e);
}
Upvotes: 0
Reputation: 391276
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:
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.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.
Upvotes: 19
Reputation: 405
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.
Upvotes: 1
Reputation: 956
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.
Upvotes: 2
Reputation: 151588
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.
Upvotes: 2