.NET & Funky Fresh

Syndication

News

  • <script type="text/javascript" src="http://ws.amazon.com/widgets/q?ServiceVersion=20070822&amp;MarketPlace=US&amp;ID=V20070822/US/bluspiconinc-20/8001/8b68bf4b-6724-40e7-99a5-a6decf6d8648"> </script>
Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
.NET Event Techniques

Yesterday I was reviewing some code created by the Prism team.  I came across a rather clever event implementation that I hadn't see before:

public event EventHandler Updated = delegate { };

protected void UpdatePrice(string tickerSymbol, decimal newPrice, long newVolume)
{
	_priceList[tickerSymbol] = newPrice;
	_volumeList[tickerSymbol] = newVolume;

	Updated(this, new MarketFeedEventArgs(tickerSymbol, newPrice, newVolume));
}

Normally I would write this code like so:

public event EventHandler Updated;

protected void UpdatePrice(string tickerSymbol, decimal newPrice, long newVolume)
{
	_priceList[tickerSymbol] = newPrice;
	_volumeList[tickerSymbol] = newVolume;

	if(Updated != null)
		Updated(this, new MarketFeedEventArgs(tickerSymbol, newPrice, newVolume));
}

I commented that this wasn't the way I was used to seeing events declared or fired and that I thought it might confuse some people.  Ayende informed me that this is the way he normally does things, stating that it was threadsafe and no fuss.  I'm thinking about adopting this technique and was wondering if I'm the only one who hasn't seen this before now.  Also, does anyone have any opinions one way or the other about this particular implementation?


Posted 03-20-2008 12:17 PM by Rob Eisenberg

[Advertisement]

Comments

DotNetKicks.com wrote .NET Event Techniques
on 03-20-2008 1:31 PM

You've been kicked (a good thing) - Trackback from DotNetKicks.com

Tim B wrote re: .NET Event Techniques
on 03-20-2008 1:34 PM

Brilliant!  Reminds me of the null object pattern.  I haven't seen this before either but I think I'll incorporate it in my mental toolbox (once I get out of @%^# 1.1 land, that is!)

Denny wrote re: .NET Event Techniques
on 03-20-2008 2:13 PM

You're not alone. I've never seen this, either. That really *does* simplify things!

JohnB wrote re: .NET Event Techniques
on 03-20-2008 2:27 PM

How would this work with VB? I would assume you would have the following:

Public Event Updated As EventHandler = AddressOf SomeAnonymousMeth

That does not work. I'm not fluent with C# so maybe you can offer some help. Thanks!

zproxy wrote re: .NET Event Techniques
on 03-20-2008 3:16 PM

It's like passing a zero length array istead of a null pointer.

Actually I think there should be some compiler support for calling in on events that could be nullpointers, in which case the call would be ommited.

I propose this syntax:

--

public event MySpecialThingHappened;

this.MySpecialThingHappened();

--

Where the event should default to System.Action and the compiler could emit the bytecode to make sure a nullpointer call would be skipped.

DaRage wrote re: .NET Event Techniques
on 03-20-2008 4:23 PM

But why would you call a method (although empty) when you don't have too? it works but it's dirty. The way i do it is by having an OnUpdated method that checks the null condition and call the handler.

Tim B wrote re: .NET Event Techniques
on 03-20-2008 5:34 PM

@DaRage: it's all about readability and usability.  It's less code to write, easier to read, and the perf hit of calling an empty method is vanishingly small anyway.  Having to check the delegate for null has always irked me. IMO the empty delegate assignment Rob describes should happen automagically and invisibly when an event is declared.

pls wrote re: .NET Event Techniques
on 03-20-2008 6:45 PM

I'm curious what is faster: always checking for null eventhandler, or calling empty function? Did you check this?

Nick wrote re: .NET Event Techniques
on 03-21-2008 2:24 AM

The disadvantage here is that you are allocating an extra object (the anonymous delegate) for each event on every instance of your object, which may seem trivial but it adds up when you have hundreds or thousands of objects.

The original design seems to be for performance reasons - events that no one signs up for don't incur any overhead, until you do sign up for an event and it becomes a MulticastDelegate or whatever.

The null check is annoying and I'm sure the language designers agree, but I view it as a necessary evil.

Yoav Sion wrote re: .NET Event Techniques
on 03-21-2008 5:14 AM

Interesting. I've checked pls's suggestion for an average of ten iterations of 10,000,000 calls to either one of the possible event calling techniques:

1. A "regular" event with no registered delegates and a null check before calling it.

2. A "ghost" event with no OTHER registered delegates and no null check before calling it.

And the results are (in average): Technique number 2 is approximately 5 times slower than the null check. (456ms vs. 88ms)

Still, I think it's worth it, unless the event is raised VERY frequently.

Thanks for the post!

Joe Brinkman wrote re: .NET Event Techniques
on 03-21-2008 6:04 AM

@JohnB - This is not required in VB because the compiler is smart enough to add this check for you.

Robert H wrote re: .NET Event Techniques
on 03-21-2008 7:33 AM

I did a small time test comparing checking for null and using the empty function. First I did it without subscribing and then with subscribing. For me the checking for null was ~2.5 times faster but I had to do a lot of iterations (100000) to get any of them to register on the millisecond scale.  I will be using the empty function approach since the checking for null thing has bothered me for quite some time.

Dew Drop - March 21, 2008 | Alvin Ashcraft's Morning Dew wrote Dew Drop - March 21, 2008 | Alvin Ashcraft's Morning Dew
on 03-21-2008 8:48 AM

Pingback from  Dew Drop - March 21, 2008 | Alvin Ashcraft's Morning Dew

Bryan Watts wrote re: .NET Event Techniques
on 03-21-2008 10:09 AM

This code is not thread-safe:

# if(Updated != null)  

#         Updated(this, new MarketFeedEventArgs(tickerSymbol, newPrice, newVolume));

There is a race condition between the check and the invoke where the handler might be removed in another thread.

You should invoke a local copy of the event:

# var local = Updated;

#

# if(local != null)  

#         local(this, new MarketFeedEventArgs(tickerSymbol, newPrice, newVolume));

which is even worse as boilerplate code.

Tim C wrote re: .NET Event Techniques
on 03-21-2008 10:30 AM

pls: In my timings I saw no difference.

James Curran wrote re: .NET Event Techniques
on 03-21-2008 10:36 AM

>> I'm curious what is faster: always checking for null eventhandler, or calling empty function?

I haven't actually checked it, but I cannot imagine a null reference check (which is essentially 2 MSIL instructions) taking longer that calling a function through a multicast delegate -- particularly since before it does call it, the delegate will do it's own null reference check (so it can throw the exception).  

And remember, it's always doing that extra call, whether there is another event attached or not.  (However, the more events attached, the smaller the proportion the time spend on this one is compared to the rest of the method)

To put this into prospective, let's pull numbers completely out of the air, and look at the example at the top of this article.

Assuming no other events attached:

The two assignment will take maybe one tick each.

The null check will take maybe two ticks, for a total of FOUR for the standard method.

on the other hand, the empty function may take one tick for the call & return, but the multicast dispatcher could easily take  35 to 75 ticks (look at it in Reflector). So were up to FORTY to eighty ticks --- 10X to 20X the standard.

Now with one real event handler attached, the numbers change a bit.

For the standard method, we have the 4 ticks we had for the empty delegate above, plus the multicast overhead, so were still in the range of 40-80 but now plus X ticks -- the time it takes that event handle to run, which is a complete wildcard.

Finally, we have the new method, with an event handler attached.  Here, we save a bit on the null comparison, but lose a slight bit more having the dispatcher loop twice instead of once.

So, the number are 4 vs ~40 and ~42+X vs ~45+X  

(keeping in mind this are totally bogus numbers, based on an educated guess)

which means if you can expect that the event will generally have a handler attached, this method will add a small delay, but it will probably get lost in the mix -- particular if the event is in response to a UI action, which will be the real time limiting factor.

On the other hand, if having an handler attached is a rare occurrence, particularly  if the event is fired a lot, (e.g. a "ItemAdded" event during the initial filling of a list), then you could be adding significant overhead.

Eddie Velasquez wrote re: .NET Event Techniques
on 03-21-2008 11:04 AM

I would say that this pattern is less efficient than checking for null since the event arguments class has to be instanciated even if it's not going to be used.

larsw wrote re: .NET Event Techniques
on 03-22-2008 3:25 PM

I think Yuval Löwy of IDesign should be credited for this "tricks". He discuss thread-safe event handling in his Programming .NET Componet (2. ed) (O'Reilly) where he introduces the anonymous delegate instance. Check out his excellent book - if I remember correctly, you will have to add some extra sugar really be sure the event handling is thread-safe.

Reflective Perspective - Chris Alcock » The Morning Brew #58 wrote Reflective Perspective - Chris Alcock &raquo; The Morning Brew #58
on 03-25-2008 3:17 AM

Pingback from  Reflective Perspective - Chris Alcock  &raquo; The Morning Brew #58

Cornel wrote re: .NET Event Techniques
on 03-25-2008 9:11 AM

So, what's the conclusion?

Kevin wrote re: .NET Event Techniques
on 03-26-2008 8:55 AM

Check out this blog posting.  This guy may have ripped you off!

www.dev102.com/.../a-new-pattern-for-event-declaration

JohnB wrote re: .NET Event Techniques
on 03-26-2008 11:23 AM

I think this guy jacked your post!

www.dev102.com/.../a-new-pattern-for-event-declaration

Rob Eisenberg wrote re: .NET Event Techniques
on 03-26-2008 5:25 PM

@John and Kevin - No big deal really.

@Cornel - My personal decision is to use the new found technique for typical scenarios.  The performance is not a concern in most cases.  If you are building something where the event perf is a serious issue, then you probably need to take some special considerations anyways.

tobsen wrote re: .NET Event Techniques
on 03-29-2008 2:12 PM
Bryan Watts, can you point out a link about additonal information regarding the racecondition and why that changes when one makes "a local copy of the event" (after all # var local = Updated; isn't really a copy... it's another variable pointing to the same address in memory as "Updated" does, so I don't get why this changes anything for mutable types, but I am curios...)
Glenn BLock wrote re: .NET Event Techniques
on 04-01-2008 3:48 AM
@John, that's OK, Rob Jacked it from us. All's fair in love and war - APRIL FOOLS :)
Vladimir Kelman wrote re: .NET Event Techniques
on 04-07-2008 12:31 PM
tobsen, your "local" variable is pointing to the same address in memory as "Updated" *until* some other thread re-assigns Updated to null (for some reason). That reassigning is what creates a danger of "race" condition if using Updated directly. If it happened after you checked Updated for being not null but before you used it - you've got a trouble. But using local is safe - nothing in outside world can reassign it, just because it is local! So, you can assign local = Updated, wait as soon as you need, check local != null, wait again - it is safe. On the other hand, public event EventHandler Updated = delegate { }; technique showed above is safe because Updated is never equal to null. It is either an empty delegate, or a reference to event handler (unless someone explicitly assigned null to it). BTW, I got into that "updated"/"local" from an excellent "Accelerated C# 2008" book.
#region wrote Events, Generics and Extension Methods
on 04-09-2008 4:47 AM

Events, Generics and Extension Methods

#region wrote Events, Generics and Extension Methods
on 04-09-2008 7:08 AM

Events, Generics and Extension Methods

Adam wrote re: .NET Event Techniques
on 04-15-2008 2:49 PM
If this is really a concern, you won't be declaring events this way anyway as it creates inefficient locks and unnecessary objects. You would instead implement add and remove and push them into a shared container among all your events (win32 event design). For 99.999% of people, their time can better be spent optimizing their nested for loops and similar ugly constructs where they find a performance problem rather than optimizing something like this. Nick said: The disadvantage here is that you are allocating an extra object (the anonymous delegate) for each event on every instance of your object, which may seem trivial but it adds up when you have hundreds or thousands of objects.
.net Pattern fuer Event Implementierung | LieberLieber Software TeamBlog wrote .net Pattern fuer Event Implementierung | LieberLieber Software TeamBlog
on 12-10-2008 4:09 AM

Pingback from  .net Pattern fuer Event Implementierung | LieberLieber Software TeamBlog

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)