是否可以在lambda表达式中定位EventHandler?
举一个简单的例子,如果我有某种按钮UI类,我是否可以编写一个函数来获取指向其Click
事件处理程序的表达式:
SomeMethod(button => button.Click);
我正在尝试消除当前用于系统的一些魔术字符串以使事件等待。 有问题的代码来自Frank Krueger撰写的博客文章 (如果你想要一些背景,那么值得一读)。
public static Task GetEventAsync(this object eventSource, string eventName) where TEventArgs : EventArgs { //... Type type = eventSource.GetType(); EventInfo ev = type.GetEvent(eventName); //... }
虽然内部的细节可能并不重要,但完整的方法允许您使用Event
触发作为Task
的完成源,使得使用await
更容易管理。 对于某个引发事件的类,您可以通过简单的调用将该事件绑定到Task
。
Task eventTask = someEventCausingObject.GetEventAsync("SomeEventHandler"); // traditionally used as someEventCausingObject.SomeEventHandler += ...; await eventTask; // Proceed back here when SomeEventHandler event is raised.
我一直在愉快地使用这个项目,但它有它的缺点,其中最大的一个是使用硬编码的事件名string
s。 这使得事件名称更改变为运行时exception,并且难以确定事件的使用。
我开始尝试创建一个允许EventHandler
作为Expression
一部分传入的版本,其目标是这样的:
await someEventCausingObject.GetEventAsync(x => x.SomeEventHandler);
…用相应的方法签名……
public static Task GetEventAsync(this TSource eventSource, Expression<Func> 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表达式的人所污染。 我希望我在这里遗漏一些明显的东西。
不,无法定位事件。 基本上事件不是真正的类型成员,而只是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
调用)取代了一个头痛(正确的事件命名)。 虽然,它将有助于重命名的东西。