是否可以在lambda表达式中定位EventHandler?

时间:2014-02-20 19:48:47

标签: c# lambda event-handling

举一个简单的例子,如果我有某种按钮UI类,我是否可以编写一个带有指向其Click事件处理程序的表达式的函数:

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

我正在尝试消除当前用于系统的一些魔术字符串以使事件等待。有问题的代码来自blog post by Frank Krueger(如果你想要一些背景,那么值得一读。)

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

尽管内部细节可能并不重要,但完整方法允许您使用Event触发作为Task的完成源,从而可以更轻松地使用await进行管理。对于某个引发事件的类,您可以通过简单的调用将该事件绑定到Task

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

我一直在为一些项目愉快地使用它,但它有它的缺点,其中最大的一个是使用硬编码的事件名string。这使得事件名称更改变为运行时异常,并且确定事件的使用是困难的。

我开始尝试制作一个版本,允许EventHandler作为Expression的一部分传入,其目标是:

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

...使用相应的方法签名......

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

不幸的是,调用代码中的lambda表达式导致编译错误:

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

考虑到事件处理程序的使用方式,这是有道理的,但我希望找到比预先指定的字符串名称更好的解决方案。似乎搜索“表达式”和“事件处理程序”的组合都会被描述为开始+=事件处理程序分配的lambda表达式的人所污染。我希望我在这里找不到明显的东西。

1 个答案:

答案 0 :(得分:1)

不,无法定位事件。基本上事件不是真正的类型成员,而只是C#语法,它产生add_EventName和remove_EventName方法对。

您可以尝试引用这些内部方法名称,但在C#中不可能 - http://msdn.microsoft.com/en-us/library/z47a7kdw.aspx

SO中有许多类似的问题,答案是否定的 - 就像Jon Skeet https://stackoverflow.com/a/4756021/2170171

如果你真的疯了,你可以试试像

这样的东西
private static void Subscribe(Action addHandler)
{
    var IL = addHandler.Method.GetMethodBody().GetILAsByteArray();

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

使用类似

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

您可以尝试使用Cecil http://www.mono-project.com/Cecil来分析IL,或者实现您自己的逻辑,因为它对于可预测的代码行来说应该不会太难。

我认为这不是一个好的解决方案,因为它只是将一个头痛(正确的事件命名)替换为另一个(正确的Subscribe调用)。虽然,它有助于重命名。