为一个事件两次查询一名代表

我最近被问到一个问题,如果我要两次查询一个处理程序会发生什么。 我来告诉你代码:

public delegate void OpenEventHandler(object sender, EventArgs e); public class MyWindow { public event OpenEventHandler Open; public void OpenWindow() { if (Open != null) { Open(this, new EventArgs()); } } } public class TwoDelegates { public static void HandleOpen(Object sender, EventArgs e) { Console.WriteLine("Birds fly"); (sender as MyWindow).Open -= HandleOpen; } public static void Run() { var window = new MyWindow(); window.Open += HandleOpen; window.Open += HandleOpen; window.OpenWindow(); Console.ReadKey(); } } 

我想知道为什么字符串仍然打印两次。 在它的开头,调用列表由两个具有相同委托引用的项组成,但在第一次运行后它被清理,仍然出现secon invocaiton。

UPDATE1:

似乎即使简单-=只删除一个条目:

  var window = new MyWindow(); window.Open += HandleOpen; window.Open += HandleOpen; Console.WriteLine(window.getHandlers().Count()); window.Open -= HandleOpen; Console.WriteLine(window.getHandlers().Count()); 

虽然在VS2010中调试模式,但是在浏览window.Open内部属性会显示0 window.Open用列表。 似乎在VS中显示的调试信息中有些神奇。

这是委托如何激活事件处理程序的问题。 它在开始关闭之前需要内部委托列表的副本。 因此,为同一事件的事件处理程序内的事件添加或删除事件处理程序仅影响该事件的未来调用,而不影响当前调用。

为了扩展Servy所说的内容,这里有一个稍微修改过的代码版本,还有一些调试助手来澄清正在发生的事情。 在HandleOpen函数中,我们在事件中删除HandleOpen之前和之后检查事件处理程序。 摘要是:你的多播中有两个HandleOpen副本,没有理由让一个Open- = HandleOpen删除它们。

 public class MyWindow { public event OpenEventHandler Open; public void OpenWindow() { if (Open != null) { Open(this, new EventArgs()); } } public Delegate[] getHandlers() { return Open.GetInvocationList(); } } public class TwoDelegates { public static void HandleOpen(Object sender, EventArgs e) { Console.WriteLine("Birds fly"); var thisWin = sender as MyWindow; var before = thisWin.getHandlers(); (sender as MyWindow).Open -= HandleOpen; var after = thisWin.getHandlers(); } public static void Run() { var window = new MyWindow(); window.Open += HandleOpen; window.Open += HandleOpen; window.OpenWindow(); Console.ReadKey(); } }