簡體   English   中英

作為參數傳遞給方法的事件處理程序不會取消注冊

[英]Event handler passed as argument to a method does not unregister

我已經用C#(如下所述)編寫了一個非常簡單的控制台測試應用程序,其中包含一個事件和一個事件處理程序。 使用多種方法可以注冊和注銷事件處理程序。

據我了解,以下兩個表達式的結果相同:

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.

因此,重要的是要意識到委托人實際上是什么。 在后台,委托是指向方法和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 -ed均上漲,但由於他們都包同樣的方法指針和object引用他們是“平等”。 另一方面, onethree包裝不同的方法指針和object引用,因此它們“不相等”。 由於兩者不相等,因此取消訂閱three不會將oneevent刪除。

Event1 -= new TestEventHandler(handler)創建一個TestEventHandler的新實例。 它在功能上可能與您首次附加事件時創建的TestEventHandler在功能上相同,但是它是內存中的不同對象,並且不會具有相同的引用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM