Create empty C# event handlers automatically
Asked Answered
S

8

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?

Silicium answered 4/12, 2008 at 13:41 Comment(1)
the trick in the accepted answer will avoid having to check for null but will not ensure thread safty. see here: stackoverflow.com/questions/1131184/…Hawkins
P
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#?)

Paternity answered 4/12, 2008 at 13:44 Comment(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.Silicium
Yes -- That post is priceless! Be sure to vote often there. They've done us all a great service.Paternity
-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.Insolation
Notably, initializing with an empty delegate only works in a class, not in a struct.Automatism
W
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 ); }
}
Whitmer answered 4/12, 2008 at 13:53 Comment(3)
Using an extension method is a beautiful solution. I cringe when the notion of initializing an empty delegate is put forth.Peralta
Wow, I thought the = delegate {} was handy when I saw it for the first time. This is +1 awesome, though. And so obvious with hindsight, dammit :)Readymix
handler?.Invoke(sender, e)Tubate
H
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");
}
Hairdresser answered 9/6, 2017 at 8:19 Comment(1)
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 gist.github.com/didii/c4e8ef021fb8b9fca7898d71eb0de79a for the code I used to test this.Kampmann
A
6

You can write is as:

MyEvent += delegate { };

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

Alliterate answered 4/12, 2008 at 13:44 Comment(1)
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.Whitmer
E
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);
  }
}
Ebba answered 31/3, 2009 at 15:11 Comment(0)
C
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.

Clavicle answered 4/12, 2008 at 13:53 Comment(1)
I agree, as I commented on leppie's answer. +1Whitmer
P
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.

Puiia answered 28/1, 2011 at 2:0 Comment(0)
T
-1

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

Tiossem answered 23/7, 2010 at 20:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.