2013-04-24 70 views
2

我最近被问了一个问题,如果我查询一个处理程序两次会发生什么。让我给你看看代码:为一个事件查询一个代表两次

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(); 
    } 
} 

我想知道为什么字符串仍然打印两次。在它的开头部分,“调用”列表由两个具有相同代表引用的项目组成,但在第一次运行后它将被清理,仍然会出现调用请求。

UPDATE1:

似乎连简单-=只删除一个条目:在VS2010

var window = new MyWindow(); 

window.Open += HandleOpen; 
window.Open += HandleOpen; 

Console.WriteLine(window.getHandlers().Count()); 
window.Open -= HandleOpen; 
Console.WriteLine(window.getHandlers().Count()); 

虽然调试模式,而你通过window.Open内部属性显示了0计数空的调用列表。看起来这在VS中显示的调试信息中有些神奇。

回答

1

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

+0

你能解释一下它有点详细吗?我基本上试图通过发送访问发件人对象来清理原始事件的invocaiton列表。首先' - ='真正清理'sender'的窗口对象'Open.NonPublic.InvocationsList'属性的调用列表,它甚至在调试窗口中显示。但'GetInvocationList()'仍然返回一个处理程序。 – 2013-04-25 08:50:42

+0

@Johnny_D这个答案的重点在于那并不重要。清除调用列表只会影响将来调用事件时运行哪些处理程序。更改在当前调用事件时将运行哪些处理程序为时已晚,因为调用列表在调用时会被复制。 – Servy 2013-04-25 13:52:55

+0

好的,很明显。但我仍然想知道现场发生了什么,为什么VS2010的行为显示空的调用列表,尽管GetInvocationList返回一个条目。 – 2013-04-25 14:09:40

1

为了扩展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(); 
    } 
} 
+0

请注意,即使'HandleOpen'有两次' - = HandleOpen',它仍然会触发第二个处理程序。这是一个完全不同的问题,删除处理程序只会删除两个实例中的一个,而不是两个。 – Servy 2013-04-24 16:13:04

+0

这是绝对正确的 - 被叫方无法修改机上事件的副本。 – 2013-04-24 16:17:04

+0

根据OP的评论,这听起来就是他想要做的。现在,似乎他认为一个' - ='将会删除这两个处理程序,所以这个答案确实很有帮助。 – Servy 2013-04-24 16:18:41