为一个事件查询一个委托两次

本文关键字:一个 两次 事件查询 | 更新日期: 2023-09-27 18:10:38

最近有人问我一个问题,如果我查询一个处理程序两次会发生什么。让我给你看代码:

 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:

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

 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计数。在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();
    }
}