【问题标题】:Can't remove methods from delegate object invocation list properly无法从委托对象调用列表中正确删除方法
【发布时间】:2015-05-13 12:39:07
【问题描述】:

正如我在这里和那里读到的,在 C# 中使用或不使用委托对象的 new 关键字在调用列表中添加或删除方法是完全相同的,并且会产生相同的 IL。例如看这个:What is the difference between two ways of using delegates in C# (with new keyword and without) 有这段代码:

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

这两者没有区别。但是当我使用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);
}

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

谢谢。

【问题讨论】:

    标签: c# delegates


    【解决方案1】:

    这里有两种不同的语法:

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

    它们做不同的事情——第一个基于方法组转换构建一个新的委托实例。这完全等同于:

    DelegateType x = MethodName;
    

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

    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
        }
    }
    

    创建一个引用另一个委托实例的新委托实例很少是一个好主意,除非它是为了委托差异 - 这也比过去通过引用转换更好地处理(有来自 @ 的引用转换例如 987654325@ 到 Action<object>,不会丢失身份)。

    【讨论】:

    • “这也比以前处理得更好” - Action<T> 不总是逆变的吗?
    • @dcastro:不——直到 .NET 4.0。从理论上讲,我相信它可能是因为 CLR 已经支持泛型变体,但我不相信相关属性曾经在标准库中应用过。
    • @JonSkeet 我刚刚在UnRegisterHandler 中测试了Equals 方法,如下所示:Console.WriteLine("Equality: {0}", _handler.Equals(methods)),但它返回了true。为什么?
    • @atoi.powered:因为它等于-它不等于new TextPrinter(methods))
    • @JonSkeet 让我用一个插图来解释一下。我给您的一些示例代码编号以便以后参考它们:(1)>>Action action1 = Main;(2)>>Action action3 = new Action(action1);(3)>>Console.WriteLine(action1.Equals(action3)); // False。现在,我认为UnRegisterHandler 的参数中发生的情况是:TextPrinter methods = PushMessage; 在语义方面与您的数字 (1) 完全相同。 _handler -= new TextPrinter(methods); // exactly as (2)Console.WriteLine("Equality: {0}", _handler.Equals(methods)); 与 (3) 完全相同。我错了吗?
    猜你喜欢
    • 2012-01-29
    • 2018-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多