Can't remove methods from delegate object invocation list properly

As I read here and there a little, adding to or removing methods from the invocation list with or without the new keyword of a delegate object in C# is exactly the same and produces the same IL. See this one for example: What is the difference between two ways of using delegates in C# (with new keyword and without) that had this fragment of code:

this.button1.Click += new System.EventHandler(this.button1_Click);
this.button1.Click += this.button1_Click;

No difference between those two. But I accidentally came across observing the unusual output with this code when I used delegate for passing as method parameter:

private delegate void TextPrinter(string text);
private static TextPrinter _handler;

static void Main(string[] args)
{
    TextPrinter myPrinter = PushMessage;
    RegisterHandler(PushMessage);
    UnRegisterHandler(PushMessage);
    InvokePrinter("hello");
}

private static void RegisterHandler(TextPrinter methods)
{
    _handler += methods;
}

private static void UnRegisterHandler(TextPrinter methods)
{
    /* first routine >> */_handler -= new TextPrinter(methods);
    /* second routine >> */ //_handler -= methods;
}

private static void InvokePrinter(string message)
{
    _handler(message);
}

private static void PushMessage(string message)
{
    Console.WriteLine("# :: {0}", message);
}

If I use the second routine in UnRegisterHandler, everything works as how it should. But when I use the first one, the PushMessage method won't be removed from the invocation list of _handler although that with or without new I guess it must have worked properly. So, what is the issue here?

Thanks.

Jon Skeet
people
quotationmark

You've got two different syntaxes here:

  • DelegateType x = new DelegateType(MethodName)
  • DelegateType x = new DelegateType(ExistingDelegateInstance)

Those do different things - the first builds a new delegate instance based on a method group conversion. That's entirely equivalent to just:

DelegateType x = MethodName;

The second builds a new delegate instance which is effectively "redirected" to the existing instance, and isn't equal to it. This is easily demonstrated:

using System;

public class Test
{
    static void Main()
    {        
        Action action1 = Main;
        Action action2 = new Action(Main);
        Action action3 = new Action(action1);

        Console.WriteLine(action1.Equals(action2)); // True
        Console.WriteLine(action1.Equals(action3)); // False
    }
}

It's very rarely a good idea to create a new delegate instance which refers to another one, unless it's for the sake of delegate variance - which is also better handled than it used to be via reference conversions (there's a reference conversion from Action<string> to Action<object> for example, with no loss of identity).

people

See more on this question at Stackoverflow