我最近学会了使用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");
}
}
在此示例中,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");
另外,从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");
这只是一个简单的例子,但我正在尝试创建一个类,它可以以最干净的方式重新发送添加到其中的事件源事件。我不想仅仅为了重新分配相同的事件来创建命名方法,而且我宁愿不存储我可以稍后从事件处理程序中取消挂起的lambda函数列表。大多数情况下,我只是好奇为什么会这样?
有什么想法吗?
答案 0 :(得分:4)
通过Raise函数将EventA的旧值捕获到闭包中。从后来你使用+ =它改变了EventA的值,但你的闭包仍然有一个旧的值。
你编码:
EventB += EventA.Raise;
EventA += (s, a) => Console.WriteLine("Event A raised");
可以扩展为等效的代码,这清楚地说明了为什么你得到旧代表:
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");)
您可以在EventB += EventA.Raise
之前添加以下内容,以验证代码是否实际为A提升了旧事件:
EventA += (s, a) => Console.WriteLine("Old Event A raised");
答案 1 :(得分:2)
委托对象不可变。很像字符串。因此,当您分配EventA时,您将创建一个 new 对象。 EventB仍然以旧版本为目标,尚未分配任何事件处理程序。您必须交换这两个语句来解决问题。