patridge
patridge

Reputation: 26565

Is it possible to target an EventHandler in a lambda expression?

For a simple example, if I had some sort of button UI class, could I write a function that takes an expression that points to its Click event handler:

SomeMethod<SomeButtonClass>(button => button.Click);

I'm trying to eliminate some magic strings currently being used for a system to make events awaitable. The code in question is derived from a blog post by Frank Krueger (a worthwhile read, if you want some background).

public static Task<TEventArgs> GetEventAsync<TEventArgs>(this object eventSource, string eventName) where TEventArgs : EventArgs {
    //...
    Type type = eventSource.GetType();
    EventInfo ev = type.GetEvent(eventName);
    //...
}

While the specifics inside probably aren't important, the full method allows you to use an Event triggering as the completion source for a Task, making it easier to manage with await. For some class that raises an event, you can tie into a Task based on that event with a simple call.

Task<EventArgs> eventTask = someEventCausingObject.GetEventAsync<EventArgs>("SomeEventHandler");
// traditionally used as someEventCausingObject.SomeEventHandler += ...;
await eventTask;
// Proceed back here when SomeEventHandler event is raised.

I have been using this happily for a couple projects, but it has its drawbacks, one of the biggest being the use of hard-coded event name strings. This makes event name changes turn into runtime exceptions, and determining usage of the event is difficult.

I started trying to make a version that would allow the EventHandler to be passed in as part of an Expression with the goal of something like this:

await someEventCausingObject.GetEventAsync<EventCausingClass, EventArgs>(x => x.SomeEventHandler);

...with the corresponding method signature...

public static Task<TEventArgs> GetEventAsync<TSource, TEventArgs>(this TSource eventSource, Expression<Func<TSource, EventHandler>> eventHandlerExpression) where TEventArgs : EventArgs {
    //...
}

Unfortunately, the lambda expression in the calling code causes a compile error:

Error CS0070: The event `SomeEventHandler' can only appear on the left hand side of += or -= when used outside of the type `EventCausingClass'.

This makes some sense given how event handlers are typically used, but I was hoping to find a better solution going forward than the pre-specified string name. It seems searches for combinations of "expression" and "eventhandler" all tend to be polluted with people describing lambda expressions for beginning += event handler assignment. I'm hoping I am missing something obvious here.

Upvotes: 3

Views: 799

Answers (1)

Lanorkin
Lanorkin

Reputation: 7514

No, it is not possible to target an event. Basically event is not a real type member, but just C# syntax which produces add_EventName and remove_EventName methods pair.

You could try refer to these internal methods name, but it's not possible in C# - http://msdn.microsoft.com/en-us/library/z47a7kdw.aspx

There are many similar questions in SO, with the same answer NO - like this one from Jon Skeet https://stackoverflow.com/a/4756021/2170171

If you're real crazy, you can try something like

private static void Subscribe(Action addHandler)
{
    var IL = addHandler.Method.GetMethodBody().GetILAsByteArray();

    // Magic here, in which we understand ClassName and EventName
    ???
}

with usage like

Subscribe(() => new Button().Click += null);

You could try using Cecil http://www.mono-project.com/Cecil for analyzing IL, or implement your own logic as it should not be too hard for predictable line of code.

I don't think that it is good solution though, as it just replaces one headache (proper event naming) with another one (proper Subscribe calling). Though, it will help with rename stuff.

Upvotes: 1

Related Questions