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

时间:2011-12-06 22:36:27

标签: c# events

  

可能重复:
  How to pass an event to a method?

是否可以将事件作为参数传递给方法?

例如,以下方法订阅事件,确实有效,并取消订阅事件:

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 */);

如果不可能,我如何以其他方式实现类似的逻辑?我应该为订阅/取消订阅方法传递一对代表吗?

3 个答案:

答案 0 :(得分:43)

事实上你发现事件不是C#中的“头等”;你无法将事件作为数据传递。您可以通过创建委托将委托传递给与接收方关联的方法作为第一类对象。您可以将对任何变量的引用作为(主要)第一类对象传递。 (我说“大多数”是因为对变量的引用不能存储在字段中,存储在数组中等等;与其他类型的数据相比,它们受到高度限制。)您可以传递类型获取它的Type对象并传递它。

但是没有办法直接传递与特定实例关联的事件,属性,索引器,构造函数或析构函数作为数据。你可以做的最好的事情是按照你的建议,从一个lambda中创建一个委托(或一对委托)。或者,获取与事件关联的反射对象,并将其与实例一起传递。

答案 1 :(得分:28)

不,不幸的是没有。

如果你看Reactive Extensions,那就会遇到类似的问题。他们使用了三种选择(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

答案 2 :(得分:3)

你试图绕过类型安全,如果不使用反射就不能这样做。我将向您展示一个更简单的例子,说明您正在尝试做什么。

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向您展示了一个更通用的解决方案,所以我将在此结束。

相关问题