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

时间:2013-04-24 15:39:07

标签: c# .net events delegates

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

 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调度的空调用列表。似乎在VS中显示的调试信息中有些神奇。

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分: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();
    }
}