Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

When is an event null?

2 views
Skip to first unread message

dve...@gmail.com

unread,
Nov 16, 2006, 8:24:58 AM11/16/06
to
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?

Jon Skeet [C# MVP]

unread,
Nov 16, 2006, 8:34:26 AM11/16/06
to

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

Bela Istok

unread,
Nov 16, 2006, 8:31:31 AM11/16/06
to
You always need to check
If(MyEvent != null)

Because an event is null when no one is subscribed.

Regards,

Bela Istok
<dve...@gmail.com> wrote in message
news:1163683498.2...@m7g2000cwm.googlegroups.com...

Dustin Campbell

unread,
Nov 16, 2006, 8:39:10 AM11/16/06
to

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.


Jon Skeet [C# MVP]

unread,
Nov 16, 2006, 9:04:09 AM11/16/06
to
Bela Istok wrote:
> You always need to check
> If(MyEvent != null)
>
> Because an event is null when no one is subscribed.

You certainly need to do the check, but in a multi-threaded environment
that's not enough.
See http://www.pobox.com/~skeet/csharp/lockchoice.shtml for more on
this.

(In environments where only single-threaded subscribe/unsubscribe/raise
are supported, the above is okay.)

Jon

JR

unread,
Nov 16, 2006, 10:54:30 AM11/16/06
to
It probably should have been

http://www.yoda.arachsys.com/csharp/threads/lockchoice.shtml

JR

"Jon Skeet [C# MVP]" <sk...@pobox.com> wrote in message
news:1163685849.0...@h54g2000cwb.googlegroups.com...

Martin Z

unread,
Nov 16, 2006, 11:11:45 AM11/16/06
to
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.

John J. Hughes II

unread,
Nov 16, 2006, 1:11:22 PM11/16/06
to
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

"Jon Skeet [C# MVP]" <sk...@pobox.com> wrote in message
news:1163685849.0...@h54g2000cwb.googlegroups.com...

Jon Skeet [C# MVP]

unread,
Nov 16, 2006, 2:14:36 PM11/16/06
to
Martin Z <martin...@gmail.com> wrote:
> 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.

--
Jon Skeet - <sk...@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Jon Skeet [C# MVP]

unread,
Nov 16, 2006, 2:15:47 PM11/16/06
to
John J. Hughes II <n...@invalid.com> wrote:
> 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.

Dale

unread,
Nov 16, 2006, 2:43:01 PM11/16/06
to
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


"Jon Skeet [C# MVP]" wrote:

John J. Hughes II

unread,
Nov 16, 2006, 2:49:58 PM11/16/06
to
Jon,

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

Regards,
John

"Jon Skeet [C# MVP]" <sk...@pobox.com> wrote in message

news:MPG.1fc6c93ed...@msnews.microsoft.com...

Martin Z

unread,
Nov 16, 2006, 3:07:22 PM11/16/06
to

Jon Skeet [ C# MVP ] wrote:

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?

Jon Skeet [C# MVP]

unread,
Nov 16, 2006, 4:27:01 PM11/16/06
to
Dale <dale...@nospam.nospam> wrote:
> 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.

Jon Skeet [C# MVP]

unread,
Nov 16, 2006, 4:32:46 PM11/16/06
to
Martin Z <martin...@gmail.com> wrote:

<snip>

> 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.)

Martin Z

unread,
Nov 16, 2006, 5:08:51 PM11/16/06
to
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.

Geek

unread,
Nov 17, 2006, 2:01:23 AM11/17/06
to
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!");

Jon Skeet [C# MVP]

unread,
Nov 17, 2006, 12:38:16 PM11/17/06
to
Geek <kanna...@gmail.com> wrote:
> 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...

Martin Z

unread,
Nov 17, 2006, 2:17:09 PM11/17/06
to
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.

Jon Skeet [C# MVP]

unread,
Nov 19, 2006, 3:30:56 AM11/19/06
to
Martin Z <martin...@gmail.com> wrote:
> 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.

Jon Skeet [C# MVP]

unread,
Nov 19, 2006, 3:32:16 AM11/19/06
to
Martin Z <martin...@gmail.com> wrote:
> 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...

It would be a complete abuse of exceptions, IMO. Not having any
subscribers is *not* an exceptional situation for an event.

(As for the "heaviness" of .NET exceptions - if you mean in terms of
performance, you might like to read
http://www.pobox.com/~skeet/csharp/exceptions.html - it's a pet topic
of mine :)

Martin Z

unread,
Nov 20, 2006, 11:42:15 PM11/20/06
to
Well, it's a matter of style. I first learned proper OOP coding in
Python, and in Python the idiom has always been "just try it and handle
the exception" rather than check-first. This works in Python because
exceptions are hardly more expensive than any other language construct.
If you keep the catch block close to the call then the legibility of
the concept does not suffer. In general, exceptions are treated as the
de-facto standard approach for any object that can return different
types of data.

However, this approach is inappropriate in C#, for three big reasons:
1) the cost of exceptions, while less than, say database access, is
non-trivial in other operations. For example, a problem in 1.1 was
parsing - no way to parse into an int without risking an exception. If
you had piles of strings coming in that you had no idea if they were
valid ints or not, and you had to check each and every one for
int-ness, and using ParseInt and handling the exception case as "no,
it's not an int", then you'd really feel the cost of exceptions (early
in my coding days I made this mistake using Hashtables in some language
or another).

2) Convention. C# coders don't expect the just-try-it idiom. Be kind
to your maintainers.

3) The debugger - C# has a handy "break on all throws" feature which is
very helpful... particularly given the utterly pathetic amount of
information returned by many exceptions, this stack-break is often the
only way to discover the true source of an error. Having code that
uses a lot of exceptions for expected events makes this sort of
debugging impossible.

So I obey the rule - but I don't like it. There are too many cases
where the "just try it" approach is the most computationally sensible
way of doing things. Remote operations, for example - testing the
viability of an operation is as expensive as the operation itself, and
thus is wasteful in those cases.

Sorry for the offtopic rant, it's just that the "exceptions for
unexpected failures ONLY" concept is considered to be a universal truth
by many programmers, and I've always found that irksome.

Jon Skeet [C# MVP]

unread,
Nov 21, 2006, 2:22:49 AM11/21/06
to
Martin Z <martin...@gmail.com> wrote:

<snip>

> Sorry for the offtopic rant, it's just that the "exceptions for


> unexpected failures ONLY" concept is considered to be a universal truth
> by many programmers, and I've always found that irksome.

It was a refreshing read. There are certainly advantages to "try it and
see" - notably atomicity. For example, if you check whether or not a
file exists and then try to read it, you need to be able to handle the
case where the file is deleted just before you open it (or can't be
read) anyway.

It just feels different when it's so easy to check for this one
condition - and where you'd often handle the single condition of there
being no handlers differently to a real, "worrying" exception being
thrown. I think that's the nub of my objection to using exceptions in a
cavalier manner - if they're reserved for "something significant is
wrong" then it's easier to decide that, for instance, all exceptions
should be logged, and whatever transaction you're in the middle of
should be aborted, etc.

I need to think about it further though...

0 new messages