Querying one delegate twice for one event

2019-05-22 11:11发布

I was recently asked a question what will happen if I would query one handler twice. Let me just show you code:

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

I wonder why string is still printed twice. On the beginning of it Invocation list consist of two items with same delegate reference, but after a first run it is cleaned up, still the secon invocaiton appears.

Update1:

Seems that even simple -= removes only one entry:

 var window = new MyWindow();

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

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

Though debug mode in VS2010, while you look through window.Open internal properties shows empty invocation list with 0 count. Seems that it's some magic in debug info displayed in VS.

2条回答
乱世女痞
2楼-- · 2019-05-22 11:21

To expand on what Servy said, here's a slightly modified version of your code, with some debugging helpers to clarify what's going on. In the HandleOpen function, we examine the event handlers before and after removing HandleOpen from the event. The summary is: There's two copies of HandleOpen in your multicast, there's no reason for a single Open-=HandleOpen to remove both of them.

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();
    }
}
查看更多
叛逆
3楼-- · 2019-05-22 11:23

It's a matter of how the delegate fires the event handlers. It takes a copy of the internal delegate list before it begins firing it off. As a result, adding or removing event handlers for an event inside of an event handler for that same event only affects future invocations of that event, not the current invocation.

查看更多
登录 后发表回答