When is an event null?

D

dvestal

Suppose I have this:

class C {
public delegate void MyEventHandler();
public event MyEventHandler MyEvent;

public void foo() {
MyEvent(); // NullReferenceException?
}
}

Under what circumstances will trying to raise the event in the foo
method generate a NullReferenceException, and why?
 
B

Bela Istok

You always need to check
If(MyEvent != null)

Because an event is null when no one is subscribed.

Regards,

Bela Istok
 
J

Jon Skeet [C# MVP]

Suppose I have this:

class C {
public delegate void MyEventHandler();
public event MyEventHandler MyEvent;

public void foo() {
MyEvent(); // NullReferenceException?
}
}

Under what circumstances will trying to raise the event in the foo
method generate a NullReferenceException, and why?

You'll get a NullReferenceException if no-one has subscribed to the
event.
By declaring a "field-like event" you get an event (which is basically
an add/remove pair) and a field of the delegate type. The field's value
is null if no-one has subscribed to it.

See http://www.pobox.com/~skeet/csharp/events.html for a rather fuller
description.

Jon
 
D

Dustin Campbell

Suppose I have this:
class C {
public delegate void MyEventHandler();
public event MyEventHandler MyEvent;
public void foo() {
MyEvent(); // NullReferenceException?
}
}
Under what circumstances will trying to raise the event in the foo
method generate a NullReferenceException, and why?

The event will be null until an event handler is actually added to it. And,
it will be null after the last event handler is removed from it. The reason
is that the code that you posted really compiles to something that looks
a little more like this:

class C {
{
public delegate void MyEventHandler();
private MyEventHandler _MyEvent;
public void add_MyEvent(MyEventHandler value)
{
_MyEvent = (MyEventHandler)Delegate.Combine(_MyEvent, value);
}
public void remove_MyEvent(MyEventHandler value)
{
_MyEvent = (MyEventHandler)Delegate.Remove(_MyEvent, value);
}
public void foo() {
MyEvent();
}
}

An event compiles to a private field that holds your delegate instance and
two methods that add and remove handlers to and from the delegate. When the
class is created, the field is null. When a handler is added to the event,
Delegate.Combine() is called and that creates your delegate or adds the handler
to your delegate if it is already created. When a handler is removed from
the event, Delegate.Remove() is called and that removes the handler from
your delegate sets it to null if there aren't anymore handlers.

Best Regards,
Dustin Campbell
Developer Express Inc.
 
M

Martin Z

Skeet, your link 404s. I can see why this would be confusing though -
the event has collection-like semantics, and one imagines that calling
the event would involve simply iterating across the list and calling
the handlers... so one would expect the empty event-handler-list would
simply be handled by iterating across an empty list - not by returning
a null complaint. I guess one could just wrap it in a generic
"CallEvent" function or something that does the null-check for you.
 
J

John J. Hughes II

Jon,

First thanks for the link.

Second a question about you suggested method of implementing an event.

If "handler = someEvent", is not "handler" a reference to "someEvent" so
when "someEvent" loses the event handler would not "handler" also lose the
event handler?

Regards,
John
 
J

Jon Skeet [C# MVP]

Martin Z said:
Skeet, your link 404s.

Yup, sorry about that - as JR pointed out, the link should be

http://www.yoda.arachsys.com/csharp/threads/lockchoice.shtml
I can see why this would be confusing though -
the event has collection-like semantics, and one imagines that calling
the event would involve simply iterating across the list and calling
the handlers... so one would expect the empty event-handler-list would
simply be handled by iterating across an empty list - not by returning
a null complaint. I guess one could just wrap it in a generic
"CallEvent" function or something that does the null-check for you.

Well, it's consistent with a collection being null as well. I agree it
would be nice if delegates had an easy way of creating an "empty"
handler list, and indeed you can start off an event that way and avoid
the null check, but the default value of a field being null *is*
consistent with other semantics.
 
J

Jon Skeet [C# MVP]

John J. Hughes II said:
First thanks for the link.

Second a question about you suggested method of implementing an event.

If "handler = someEvent", is not "handler" a reference to "someEvent" so
when "someEvent" loses the event handler would not "handler" also lose the
event handler?

No, "handler" and "someEvent" are both variables. After the assignment,
if "someEvent" changes to refer to a different delegate (or null) that
won't change the value of "handler".

Note that delegates are immutable - using += and -= doesn't change the
list of handlers within a specific delegate, it returns a *new*
delegate with a different list.
 
G

Guest

Jon,

In the "correct" example on your page, what prevents handler from being null
after the lock section is exited?

Dale

--
Dale Preston
MCAD C#
MCSE, MCDBA
 
J

John J. Hughes II

Jon,

So its a copy... thanks again. This should fix a problem I fixed with
try/catch :)

Regards,
John
 
M

Martin Z

Jon said:
No, "handler" and "someEvent" are both variables. After the assignment,
if "someEvent" changes to refer to a different delegate (or null) that
won't change the value of "handler".

Note that delegates are immutable - using += and -= doesn't change the
list of handlers within a specific delegate, it returns a *new*
delegate with a different list.

Good god, I never realised they're immutable. Too used to C++ where +=
doesn't necessarily mean "sum and reassign", particularly when they've
been creatively-overloaded... here in C#, the += should be a dead
giveaway that it's immutable semantics. I feel silly now. With +=
behaviour, it's no wonder they're null-when-empty.

In that case, do events actually support being added to each other?
Could I do event1+event2 to get the union of the event handlers of
event1 and event2?
 
J

Jon Skeet [C# MVP]

Dale said:
In the "correct" example on your page, what prevents handler from being null
after the lock section is exited?

It might be - which is why there's the check for nullity. The important
thing is that it won't change from non-null to null *after* the check
for nullity.
 
J

Jon Skeet [C# MVP]

Good god, I never realised they're immutable. Too used to C++ where +=
doesn't necessarily mean "sum and reassign", particularly when they've
been creatively-overloaded... here in C#, the += should be a dead
giveaway that it's immutable semantics. I feel silly now. With +=
behaviour, it's no wonder they're null-when-empty.

No need to feel silly :)
In that case, do events actually support being added to each other?
Could I do event1+event2 to get the union of the event handlers of
event1 and event2?

You can't do it with events as such, but you *can* do it with
delegates. In fact, it's not the union, but the addition of two lists -
if the same handler is in both delegates, it will be there *twice* in
the combined list.

Have a look at
http://www.pobox.com/~skeet/csharp/events.html for more on that kind of
thing, and the differences between delegates and events. (C#'s way of
handling them can make it tricky to see the differences.)
 
M

Martin Z

Thanks for the link - it all makes much more sense now. I guess the
problem is that I always imagine that Delegates and Events work the way
I'd have designed them, not the way they're designed. I always
imagined it being something Java-ish, with Delegate types being simple
interfaces and delegate instances being some sort of implicit inner
class, a-la Java... and Events being a simple collection of said
objects. Now that I can see the value-type semantics (somebody at MS
has a fetish for those don't they?) it all makes so much more sense.
Still sad that one has to do so much boilerplate to get the best
threadsafe behaviour.
 
G

Geek

if u dont subscribe the ur event it'll throw nullreference exception.
have a look at this following code which handling the situation when
ur event is not subscribed!.

using System;
using System.Collections.Generic;
using System.Text;

namespace Sapmle_Events
{
class Program
{
public delegate void MyDelegate();
static event MyDelegate Myevent;
static void Program_Myevent()
{
Console.WriteLine("MyEvent Triggered!");
}
static void Main(string[] args)
{
//subscribing the myevent
Myevent += new MyDelegate(Program_Myevent);
Trigger_Myevent();
Console.ReadKey();
}
static void Trigger_Myevent()
{
if (Myevent!=null)
{
Myevent();
}
else
{
Console.WriteLine("Myevent not subscribed!");
}
}

}
}
 
J

Jon Skeet [C# MVP]

Geek said:
if u dont subscribe the ur event it'll throw nullreference exception.
have a look at this following code which handling the situation when
ur event is not subscribed!.

So long as another thread doesn't make it null between the evaluation
of the condition and you calling it...
 
M

Martin Z

Too bad DotNet exceptions are so cumbersome (heavyweight, difficult to
mask out in debugger) or I'd just suggest doing the Pythonesque
approach of "just do it and catch the exception"... well, that and the
fact that a NullReferenceException could also come from the actual
events being called, the hiding of which would be a Very Bad Thing....
any handling logic to try and discern what object was null that threw
the exception would have the same threading problems as the original
plan...

Yeah, your approach is the best, the "lock/copy and then run the copy"
idiom is nice.
 
J

Jon Skeet [C# MVP]

Martin Z said:
Thanks for the link - it all makes much more sense now. I guess the
problem is that I always imagine that Delegates and Events work the way
I'd have designed them, not the way they're designed. I always
imagined it being something Java-ish, with Delegate types being simple
interfaces and delegate instances being some sort of implicit inner
class, a-la Java... and Events being a simple collection of said
objects. Now that I can see the value-type semantics (somebody at MS
has a fetish for those don't they?) it all makes so much more sense.
Still sad that one has to do so much boilerplate to get the best
threadsafe behaviour.

It's a bit easier if you create an empty event handler as your initial
value. You still need to either make it volatile or use a lock though,
in order to make sure you see the most recent value.

On the other hand, it's worth considering whether or not your events
*need* to be thread-safe. Lots of events are only ever raised,
subscribed to and unsubscribed from on the same thread.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

How can these lines be rewritten 15
Raising and event 3
Asynchronous event 2
Handling Events in C#.NET 5
Use of event handling 6
Events and Thread Safety 5
Event Subscription. Why? 8
MyEventHandler issue 3

Top