Create empty C# event handlers automatically

梦想与她 提交于 2019-11-27 10:19:30
Dinah

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#?)

TcKs

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 ); }
}

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");
}

You can write is as:

MyEvent += delegate { };

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

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);
  }
}
mcintyre321

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.

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.

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!