[英]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
one
和two
都是不同的对象引用,因为我们new
-ed均上涨,但由于他们都包同样的方法指针和object
引用他们是“平等”。 另一方面, one
和three
包装不同的方法指针和object
引用,因此它们“不相等”。 由于两者不相等,因此取消订阅three
不会将one
从event
删除。
Event1 -= new TestEventHandler(handler)
创建一个TestEventHandler的新实例。 它在功能上可能与您首次附加事件时创建的TestEventHandler在功能上相同,但是它是内存中的不同对象,并且不会具有相同的引用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.