将事件作为参数传递给方法

本文关键字:方法 参数传递 事件 | 更新日期: 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向您展示了一个更通用的解决方案,所以我将在这里结束。