将事件作为参数传递给方法
本文关键字:方法 参数传递 事件 | 更新日期: 2023-09-27 18:16:53
可能重复:
如何将事件传递给方法?
是否可以将事件作为参数传递给方法?
例如,以下方法订阅事件、工作并取消订阅事件:
void SubscribeDoAndUnsubscribe<TElement, TEventArgs>(
IEnumerable<TElement> elements,
??? elementEvent)
where TEventArgs: EventArgs
{
EventHandler<TEventArgs> handler = (sender, e) => { /* Handle an event */ };
foreach (var element in elements)
{
// Subscribe somehow
element.elementEvent += handler
}
// Do things
foreach (var element in elements)
{
// Unsubscribe somehow
element.elementEvent -= handler
}
}
客户代码:
var elements = new [] { new Button(), new Button() };
SubscribeDoAndUnsubscribe(elements, ??? /* e => e.Click */);
如果不可能,我如何通过其他方式实现类似的逻辑?我可以让两名代表通过订阅/取消订阅方法吗?
事实上,您已经发现事件在C#中不是"第一类";不能将事件作为数据传递。通过进行委托,可以将委托传递给与接收器关联的方法,作为一级对象。您可以将对任何变量的引用作为(主要(一级对象传递。(我说"主要"是因为对变量的引用不能存储在字段、数组等中;与其他类型的数据相比,它们受到高度限制。(您可以通过获取类型的type对象并传递它来传递它。
但是,没有办法直接将与特定实例关联的事件、属性、索引器、构造函数或析构函数作为数据传递。正如您所建议的,您能做的最好的事情就是从lambda中生成一个委托(或一对委托(。或者,获取与事件关联的反射对象,并将其与实例一起传递。
没有,很遗憾没有。
如果你看看反应式扩展,它也会遇到类似的问题。他们使用的三个选项(IIRC-我已经有一段时间没有看了(:
- 传入相应的
EventInfo
并用反射调用它 - 传入事件的名称(如有必要,还传入目标(并使用反射调用它
- 传入用于订阅和取消订阅的代理
后一种情况下的调用类似于:
SubscribeAndDoUnsubscribe(elements,
handler => e.Click += handler,
handler => e.Click -= handler);
声明将是:
void SubscribeDoAndUnsubscribe<TElement, TEventArgs>(
IEnumerable<TElement> elements,
Action<EventHandler<TEventArgs>> subscription,
Action<EventHandler<TEventArgs>> unsubscription)
where TEventArgs: EventArgs
您正试图绕过类型安全性,如果不使用反射就无法做到这一点。我将向你展示一个更简单的例子,说明你正在尝试做什么
void DoSomethingOnSomethingElse(T obj, Action method)
{
obj.method();
}
C#不是这样工作的。编译器如何知道所有的T
都有方法method
?它没有,也不能。类似地,并不是代码中的每个TElement
都会有一个事件Click
。
听起来您只是想在一组对象上设置一个一次性事件处理程序。你可以很容易地做到这一点。。。
EventHandler handler = null;
handler = (s,e) =>
{
DoSomething(e);
var b = (Button) s;
b.Click -= handler;
}
foreach (var button in buttons)
{
button.Click += handler;
}
很明显,这只适用于按钮,但在我写这篇文章的时候,我看到Jon Skeet向您展示了一个更通用的解决方案,所以我将在这里结束。