.NET Forum / Languages / C# / November 2006
When is an event null?
|
|
Thread rating:  |
dvestal@gmail.com - 16 Nov 2006 13:24 GMT 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?
Bela Istok - 16 Nov 2006 13:31 GMT You always need to check If(MyEvent != null)
Because an event is null when no one is subscribed.
Regards,
Bela Istok
> Suppose I have this: > [quoted text clipped - 9 lines] > Under what circumstances will trying to raise the event in the foo > method generate a NullReferenceException, and why? Jon Skeet [C# MVP] - 16 Nov 2006 14:04 GMT > 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 - 16 Nov 2006 15:54 GMT It probably should have been
http://www.yoda.arachsys.com/csharp/threads/lockchoice.shtml
JR
>> You always need to check >> If(MyEvent != null) [quoted text clipped - 10 lines] > > Jon Martin Z - 16 Nov 2006 16:11 GMT 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.
> > You always need to check > > If(MyEvent != null) [quoted text clipped - 10 lines] > > Jon Jon Skeet [C# MVP] - 16 Nov 2006 19:14 GMT > 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 [quoted text clipped - 3 lines] > 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.
 Signature Jon Skeet - <skeet@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
John J. Hughes II - 16 Nov 2006 18:11 GMT 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
>> You always need to check >> If(MyEvent != null) [quoted text clipped - 10 lines] > > Jon Jon Skeet [C# MVP] - 16 Nov 2006 19:15 GMT > First thanks for the link. > [quoted text clipped - 3 lines] > 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.
 Signature Jon Skeet - <skeet@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
Dale - 16 Nov 2006 19:43 GMT Jon,
In the "correct" example on your page, what prevents handler from being null after the lock section is exited?
Dale
 Signature Dale Preston MCAD C# MCSE, MCDBA
> > First thanks for the link. > > [quoted text clipped - 11 lines] > list of handlers within a specific delegate, it returns a *new* > delegate with a different list. Jon Skeet [C# MVP] - 16 Nov 2006 21:27 GMT > 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.
 Signature Jon Skeet - <skeet@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
John J. Hughes II - 16 Nov 2006 19:49 GMT Jon,
So its a copy... thanks again. This should fix a problem I fixed with try/catch :)
Regards, John
>> First thanks for the link. >> [quoted text clipped - 12 lines] > list of handlers within a specific delegate, it returns a *new* > delegate with a different list. Martin Z - 16 Nov 2006 20:07 GMT Jon Skeet [ C# MVP ] wrote:
> > First thanks for the link. > > [quoted text clipped - 16 lines] > http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet > If replying to the group, please do not mail me too 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] - 16 Nov 2006 21:32 GMT <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.)
 Signature Jon Skeet - <skeet@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
Martin Z - 16 Nov 2006 22:08 GMT 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.
Jon wrote:
> <snip> > [quoted text clipped - 24 lines] > 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] - 19 Nov 2006 08:30 GMT > 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 [quoted text clipped - 6 lines] > 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.
 Signature Jon Skeet - <skeet@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] - 16 Nov 2006 13:34 GMT > Suppose I have this: > [quoted text clipped - 9 lines] > 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
Dustin Campbell - 16 Nov 2006 13:39 GMT > Suppose I have this: > [quoted text clipped - 7 lines] > 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.
Geek - 17 Nov 2006 07:01 GMT 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!"); } }
} }
> Suppose I have this: > [quoted text clipped - 9 lines] > Under what circumstances will trying to raise the event in the foo > method generate a NullReferenceException, and why? Jon Skeet [C# MVP] - 17 Nov 2006 17:38 GMT > 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...
 Signature Jon Skeet - <skeet@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
Martin Z - 17 Nov 2006 19:17 GMT 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 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 [quoted text clipped - 7 lines] > 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] - 19 Nov 2006 08:32 GMT > Too bad DotNet exceptions are so cumbersome (heavyweight, difficult to > mask out in debugger) or I'd just suggest doing the Pythonesque [quoted text clipped - 4 lines] > 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 :)
 Signature Jon Skeet - <skeet@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
Martin Z - 21 Nov 2006 04:42 GMT 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 wrote:
> > Too bad DotNet exceptions are so cumbersome (heavyweight, difficult to > > mask out in debugger) or I'd just suggest doing the Pythonesque [quoted text clipped - 17 lines] > 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] - 21 Nov 2006 07:22 GMT <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...
 Signature Jon Skeet - <skeet@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
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|