作为参数传递给方法的事件处理程序不会取消注册

时间:2014-12-11 15:31:39

标签: c# events delegates

我在C#中编写了一个非常简单的控制台测试应用程序(附在下面),其中包含一个Event和一个Event处理程序。事件处理程序使用多种方式注册和取消注册。

据我所知,以下两个表达式相同:

Event1 += handler;
Event1 += new TestEventHandler(handler);

现在我主要期望这两个表达式相同:

Event1 -= handler;
Event1 -= new TestEventHandler(handler);

我依赖处理器在两种情况下都未注册。但似乎存在差异:它只是不同,当我将事件处理程序作为参数传递给方法时,当使用方法中的代码实现时,处理程序不会取消注册-= new TestEventHandler(handler)。但是,如果代码直接使用-= handler,则它可以正常工作。

我不明白。这是为什么?

现在跟随整个代码(您可以将其直接复制并粘贴到Visual Studio),然后在下面找到我得到的输出:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EventDemo1
{
    class Program
    {
        static void Main(string[] args)
        {
            Test test = new Test();
            test.Run();
            Console.ReadLine();
        }
    }

    class TestEventArgs : System.EventArgs
    {
        public TestEventArgs() { }
        public TestEventArgs(int no) { No = no; }
        public int No;
    }

    delegate void TestEventHandler(TestEventArgs e);

    public class Test
    {
        public Test()
        {
        }

        void TestHandler(TestEventArgs e)
        {
            Console.WriteLine("No={0}", e.No);
        }

        event TestEventHandler Event1;

        public void Run()
        {
            Console.WriteLine("START");
            Event1 += TestHandler;
            if (Event1 != null)
                Event1(new TestEventArgs(1)); //1
            Event1 -= TestHandler; // Uses: Event1 -= TestHandler;
            if (Event1 != null)
                Event1(new TestEventArgs(2)); //2
            Console.WriteLine("---------- OK. (Event1 == null) is {0}.", Event1 == null);

            Console.WriteLine("START");
            Event1 += new TestEventHandler(TestHandler);
            if (Event1 != null)
                Event1(new TestEventArgs(3)); //3
            Event1 -= new TestEventHandler(TestHandler); // Uses: Event1 -= new TestEventHandler(TestHandler);
            if (Event1 != null)
                Event1(new TestEventArgs(4)); //4
            Console.WriteLine("---------- OK. (Event1 == null) is {0}.", Event1 == null);

            Console.WriteLine("START using Register1/Unregister1");
            RegisterHandler1(TestHandler);
            if (Event1 != null)
                Event1(new TestEventArgs(5)); //5
            UnregisterHandler1(TestHandler); // Uses: Event1 -= TestHandler; where TestHandler is used directly.
            if (Event1 != null)
                Event1(new TestEventArgs(6)); //6
            Console.WriteLine("---------- OK. (Event1 == null) is {0}.", Event1 == null);

            Console.WriteLine("START using Register2/Unregister2");
            RegisterHandler2(TestHandler);
            if (Event1 != null)
                Event1(new TestEventArgs(7)); //7
            UnregisterHandler2(TestHandler); // Uses: Event1 -= new TestEventHandler(handler); where handler was passed as parameter
            if (Event1 != null)
                Event1(new TestEventArgs(8)); //8
            Console.WriteLine("---------- Not OK. (Event1 == null) is {0}.", Event1 == null);
            Console.WriteLine("           I expected that number 8 should not occur, but it does.");

            Console.WriteLine("END.");
        }

        private void RegisterHandler1(TestEventHandler handler)
        {
            Event1 += handler;
        }

        private void UnregisterHandler1(TestEventHandler handler)
        {
            Event1 -= handler;
        }

        private void RegisterHandler2(TestEventHandler handler)
        {
            Event1 += new TestEventHandler(handler);
        }

        private void UnregisterHandler2(TestEventHandler handler)
        {
            Event1 -= new TestEventHandler(handler);
        }
    }

}

输出:

START
No=1
---------- OK. (Event1 == null) is True.
START
No=3
---------- OK. (Event1 == null) is True.
START using Register1/Unregister1
No=5
---------- OK. (Event1 == null) is True.
START using Register2/Unregister2
No=7
No=8
---------- Not OK. (Event1 == null) is False.
           I expected that number 8 should not occur, but it does.
END.

2 个答案:

答案 0 :(得分:0)

因此,了解委托实际是什么非常重要。在幕后,委托是指向方法和object引用的指针。当您编写代码new TestEventHandler(TestHandler)时,您需要创建一个指向TestHandler并使用this作为object引用的委托。

当你写new TestEventHandler(handler)时,你正在创建一个委托,其方法是来调用handler ,而对象是handler

委托的Equals方法比较方法指针和它们各自存储的object引用是否相等。

我们现在可以编写一个较短的测试用例来检查这个问题:

var one = new TestEventHandler(TestHandler);
var two = new TestEventHandler(TestHandler);
var three = new TestEventHandler(one);

Console.WriteLine(object.ReferenceEquals(one, two));
Console.WriteLine(one.Equals(two));
Console.WriteLine(one.Equals(three));

这将打印:

false
true
false

onetwo两者都是不同的对象引用,因为我们new - 同时向上,但由于它们都包含相同的方法指针并且object引用它们是"等于"。另一方面,onethree包含不同的方法指针和object引用,因此它们不等于"。由于它们并不相同,因此使用three取消订阅不会从one删除event

答案 1 :(得分:-1)

Event1 -= new TestEventHandler(handler)创建一个TestEventHandler的新实例。它可能在功能上与您第一次附加事件时创建的TestEventHandler相同,但它在内存中是一个不同的对象,并且不会有相同的引用。