Safely firing an event, Part 2

A while back I wrote about Safely firing an event.


I come to you now with another solution to the problem, which I like quite a bit more.


In Properties with events: another attempt, we tried initializing an event with an empty anonymous method.  It works, but there’s always a risk that one of your constructors won’t do the right thing.


It turns out that you can use a field initializer on an event.  I don’t know why I didn’t try this yesterday, its seems obvious now!


            public event System.EventHandler MyEvent = delegate { };


                  c.MyEvent(this, EventArgs.Empty); // no null check required!

Voila!  The event will never be null.


There may be a small perf hit, but until my perf analysis tells me that this specific instance is a perf problem, I’m fine as-is.


If your class wants to check if there are any listeners, it can no longer do:


                  if (c.MyEvent == null)


                        // no one listens to me.



Instead you’d have to do:


                  // 1 because it always contains the empty delegate

                  if (c.MyEvent.GetInvocationList().Length == 1)


                        // no one listens to me.



If I am ever inclined to write such code, I will first think about changing my design so the problem goes away, and second do some Extract Method Refactorings to hide the nasty bits.

Comments (12)

  1. Eric Newton says:

    why would i ever use method 2 over testing for null?

    method 1 does one simple test… whereas method 2 has to call a virtual method to retrieve an array which has to provide the length of invokers and what not…

    seriously man, why would you EVER favor method2 over simply checking for null???

  2. Thomas Eyde says:

    This must be the one place the C# makers stepped without Anders Hejlsberg’s knowledge. Either that or he’s getting old.

    Why on Earth do I have to check a language construct? Even worse, why do I have to check a language construct I should have complete control over?

    The event is created in my class. How come my clients have all the benefits with simple usages, why I have to do all these null checks and race condition hacks?

    The event construct in C# is not very OO. I mean, you fire an event because you don’t know who’s listening. Why should my code, then, depend on wether someone is listening or not?

  3. Adi Robinson says:

    This is an implementation of the NullObject pattern, which has some applicability in some situations, but admittedly not all. Checking for null is very action orientated when really the <b>intention</b> of the eventing class is to simply tell listening observers about a particular event. O-O in general has it’s costs, in C# just as much as any language, especially with the dynamic dispatch. PErsonally, I do do a check for null. If you want to make the code more intention revealing I’d proabbly do a extract method refactoring or introduce explaining variable:

    bool thereArePriceUpdateListeners = (PriceUpdate != null ) ;

    if ( thereArePriceUpdateListeners ) {

    OnPriceUpdate(…) ;

    This makes the code more readable, the intentions are clear and no dynmaic dispatch costs.

    An alternative pattern is to use the State pattern to achieve the same goal.

  4. Simpler, so better.

    I was talking with someone about C#’s baked in robustness last night. This is one area where they missed the boat.

    Like you, I would gladly take robustness over "potential" performance issues. Now if only I could do this in my code today!

  5. Ron says:

    I agree with the other comments, the null check for events should be built into the Framework. However, this is a nice workaround. Too bad it has to wait for VS2005 in order to do this "cleanly".

  6. Udo says:

    Why I cant compile this?

    In the line

    public event System.EventHandler MyEvent = delegate { };

    the compiler says "Invalid expression term ‘delegate'(C1525)". Have you got a running full example?


  7. jaybaz [MS] says:

    Udo: You’ll need Whidbey (VS 2005) to do this.

    Try it out:

Skip to main content