74

It is not possible to fire an event in C# that has no handlers attached to it. So before each call it is necessary to check if the event is null.

if ( MyEvent != null ) {
  MyEvent( param1, param2 );
}

I would like to keep my code as clean as possible and get rid of those null checks. I don't think it will affect performance very much, at least not in my case.

MyEvent( param1, param2 );

Right now I solve this by adding an empty inline handler to each event manually. This is error prone, since I need to remember to do that etc.

void Initialize() {
  MyEvent += new MyEvent( (p1,p2) => { } );
}

Is there a way to generate empty handlers for all events of a given class automatically using reflection and some CLR magic?

Tomas Andrle
  • 13,132
  • 15
  • 75
  • 92
  • the trick in the accepted answer will avoid having to check for null but will not ensure thread safty. see here: http://stackoverflow.com/questions/1131184/c-initializing-an-event-handler-with-a-dummy/1131204#1131204 –  Apr 14 '10 at 21:41

8 Answers8

162

I saw this on another post and have shamelessly stolen it and used it in much of my code ever since:

public delegate void MyClickHandler(object sender, string myValue);
public event MyClickHandler Click = delegate {}; // add empty delegate!

//Let you do this:
public void DoSomething() {
    Click(this, "foo");
}

//Instead of this:
public void DoSomething() {
    if (Click != null) // Unnecessary!
        Click(this, "foo");
}

* If anyone knows the origin of this technique, please post it in the comments. I really do believe in the source getting due credit.

(Edit: I got it from this post Hidden Features of C#?)

Community
  • 1
  • 1
Dinah
  • 52,922
  • 30
  • 133
  • 149
  • 4
    Add empty delegate, right there! That's even better than I was hoping for. Thanks! I'm going to read the "hidden features" post right now. – Tomas Andrle Dec 04 '08 at 14:02
  • 1
    Yes -- That post is priceless! Be sure to vote often there. They've done us all a great service. – Dinah Dec 04 '08 at 14:04
  • 7
    -1 for two reasons: 1) this technique incurs both runtime performance and memory overhead and 2) this technique is error prone, especially compared to the extension method described below. Looking at the invocation site alone is not enough to determine the correctness of this method, but the extension method works for all events whether or not the event was initialized with an empty delegate. – Sam Harwell May 05 '13 at 19:17
  • Notably, initializing with an empty delegate only works in a `class`, not in a `struct`. – sebrockm Oct 21 '19 at 21:30
61

The notation:

if ( MyEvent != null ) {
  MyEvent( param1, param2 );
}

is not thread safe. You should do it this way:

EventHandler handler = this.MyEvent;
if ( null != handler ) { handler( param1, param2 ); }

I understand, that this is a bother, so you can do helper method:

static void RaiseEvent( EventHandler handler, object sender, EventArgs e ) {
    if ( null != handler ) { handler( sender, e ); }
}

and then call:

RaiseEvent( MyEvent, param1, param2 );

If you are using C# 3.0, you can declare helper method as extension method:

static void Raise( this EventHandler handler, object sender, EventArgs e ) {
    if ( null != handler ) { handler( sender, e ); }
}

and then call:

MyEvent.Raise( param1, param2 );

Also you can create next extension/helper methods for other event handlers. For example:

static void Raise<TEventArgs>( this EventHandler<TEventArgs> handler,
    object sender, TEventArgs e ) where TEventArgs : EventArgs
{
    if ( null != handler ) { handler( sender, e ); }
}
nawfal
  • 70,104
  • 56
  • 326
  • 368
TcKs
  • 25,849
  • 11
  • 66
  • 104
11

In C# 6.0 there's no need to go to any of these lengths to do the null check, thanks to the conditional null operator ?.

The docs explain that calling MyEvent?.Invoke(...) copies the event to a temporary variable, performs the null check, and if not null, calls Invoke on the temporary copy. This isn't necessarily thread-safe in every sense, as someone could have added a new event after the copy to the temporary variable, which wouldn't be called. It does guarantee you won't call Invoke on null though.

In short:

public delegate void MyClickHandler(object sender, string myValue);
public event MyClickHandler Click;

public void DoSomething() {
    Click?.Invoke(this, "foo");
}
gandaliter
  • 9,863
  • 1
  • 16
  • 23
  • 3
    I'll also add onto this that the performance difference of `MyEvent?.Invoke(...)` and `MyEvent(...)` with an empty delegate is significant according to my tests: the `.?` is about ~40% faster than the empty delegate method. See https://gist.github.com/didii/c4e8ef021fb8b9fca7898d71eb0de79a for the code I used to test this. – Didii Aug 07 '19 at 11:32
6

You can write is as:

MyEvent += delegate { };

I am not sure what you want to do is correct.

leppie
  • 115,091
  • 17
  • 196
  • 297
  • I realy believe, that adding empty delegate to EVERY event is right way, how to develop applications. But I believe, there can be situation, where is the quick & easy solution how to handle something. – TcKs Dec 04 '08 at 13:55
6

You don't need several extension methods for different event handlers, you just need one:

public static class EventHandlerExtensions {
  public static void Raise<T>(this EventHandler<T> handler, object sender, T args) where T : EventArgs {
    if (handler != null) handler(sender, args);
  }
}
vkelman
  • 1,501
  • 1
  • 15
  • 25
2

This is a bad idea in that the code which is consuming the event now has an expectation that the object with the event has been coded with an action by default. If your code is never going to be used anywhere else by anyone else then I guess you can get away with it.

Dinah
  • 52,922
  • 30
  • 133
  • 149
mcintyre321
  • 12,996
  • 8
  • 66
  • 103
1

C# event declarations unfortunately include a number of well-known safety problems and inefficiencies. I designed a number of extension methods on delegates to invoke them safely, and to register/unregister delegates in a thread-safe manner.

Your old event-raising code:

if (someDelegate != null) someDelegate(x, y, z);

Your new code:

someDelegate.Raise(x, y, z);

Your old event registration code:

event Action fooEvent;
...
lock (someDummyObject) fooEvent += newHandler;

Your new code:

Action fooEvent;
...
Events.Add(ref fooEvent, newHandler);

No locking needed, no compiler-inserted dummy objects used to lock the events.

naasking
  • 2,514
  • 1
  • 27
  • 32
-1

You can use PostSharp to on build time add this magic. It is the best way.

Jairo
  • 19
  • 1