无法正确删除委托对象调用列表中的方法

ato*_*red 1 c# delegates

正如我在这里和那里读到的那样,new在C#中添加或删除调用列表中的方法(有或没有委托对象的关键字)是完全相同的,并产生相同的IL.例如,请参阅此示例:在C#中使用委托的两种方式(使用new关键字和不使用)具有此代码片段有什么区别:

this.button1.Click += new System.EventHandler(this.button1_Click);
this.button1.Click += this.button1_Click;
Run Code Online (Sandbox Code Playgroud)

这两者之间没有区别.但是当我用作delegate传递方法参数时,我意外地遇到了使用此代码观察异常输出:

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);
}
Run Code Online (Sandbox Code Playgroud)

如果我使用第二个例程UnRegisterHandler,一切都按照它应该的方式工作.但是当我使用第一个PushMessage方法时,该方法将不会从调用列表中删除,_handler尽管有或没有,new我猜它必须正常工作.那么,这里的问题是什么?

谢谢.

Jon*_*eet 5

你在这里有两种不同的语法:

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

那些做不同的事情 - 第一个基于方法组转换构建新的委托实例.这完全等同于:

DelegateType x = MethodName;
Run Code Online (Sandbox Code Playgroud)

第二个构建一个新的委托实例,它有效地"重定向"到现有实例,并且不等于它.这很容易证明:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

这是非常罕见的好主意,创建引用到另一个新的委托实例,除非它是委托方的缘故-这也是更好地处理比以前要通过引用转换(有一个引用转换从Action<string>Action<object>例如,没有失去身份).