使用C#扩展方法处理事件

Ben*_*ith 6 c# windows events lambda extension-methods

我最近学会了使用C#扩展方法来简化调用事件,并且我越来越多地使用它们.我最近遇到了一个我不理解的奇怪问题,我想知道是否有人可以解释它.

尝试将eventhandler扩展方法设置为另一个事件的事件处理程序时,会出现此问题.这是我正在做的一个例子:

public static class EventHandlerExtensions
{
    public static void Raise<TEventArgs>(
        this EventHandler<TEventArgs> eventHandler, 
        object sender, TEventArgs args)  where TEventArgs:EventArgs
    {
        if (eventHandler != null)
        {
            eventHandler(sender, args);
        }
    }
}

public class Test
{
    private event EventHandler<EventArgs> EventA;
    private event EventHandler<EventArgs> EventB;

    public Test()
    {
        Console.WriteLine("::Start");
        EventB += EventA.Raise;
        EventA += (s, a) => Console.WriteLine("Event A raised");
        EventB.Raise(this, EventArgs.Empty);
        Console.WriteLine("::End");
    }
}
Run Code Online (Sandbox Code Playgroud)

在此示例中,EventA应在触发EventB时触发.但是,当我运行此代码时,EventB会触发,但A上的扩展方法找不到任何侦听器.

如果我改变订单,一切正常:

Console.WriteLine("::Start");
EventA += (s, a) => Console.WriteLine("Event A raised");
EventB += EventA.Raise;
EventB.Raise(this, EventArgs.Empty);
Console.WriteLine("::End");
Run Code Online (Sandbox Code Playgroud)

此外,从lambda调用EventA.Raise工作正常:

Console.WriteLine("::Start");
EventB += (s, a) => EventA.Raise(s, a);
EventA += (s, a) => Console.WriteLine("Event A raised");
EventB.Raise(this, EventArgs.Empty);
Console.WriteLine("::End");
Run Code Online (Sandbox Code Playgroud)

这只是一个简单的例子,但我正在尝试创建一个类,它可以以最干净的方式重新发送添加到其中的事件源事件.我不想仅仅为了重新分配相同的事件来创建命名方法,而我宁愿不存储我可以稍后从事件处理程序中取消挂起的lambda函数列表.大多数情况下,我只是好奇为什么会这样?

有任何想法吗?

Ale*_*kov 4

您可以通过 Raise 函数将 EventA 的旧值捕获到闭包中。因为稍后您使用 += 它会更改 EventA 的值,但您的闭包仍然具有旧值。

你的代码:

EventB += EventA.Raise;
EventA += (s, a) => Console.WriteLine("Event A raised"); 
Run Code Online (Sandbox Code Playgroud)

可以扩展到等效的代码,这清楚地表明为什么你得到旧的委托:

var oldEventA = EventA;
EventB += oldEventA.Raise; // captures old value here
// now EventA changed to new value 
EventA = oldEventA + ((s, a) => Console.WriteLine("Event A raised");)
Run Code Online (Sandbox Code Playgroud)

您可以在之前添加以下内容EventB += EventA.Raise来验证代码是否确实引发了 A 的旧事件:

EventA += (s, a) => Console.WriteLine("Old Event A raised");
Run Code Online (Sandbox Code Playgroud)