C# 作为参数传递给方法的事件处理程序不会取消注册
我用C#(附在下面)编写了一个非常简单的控制台测试应用程序,其中包含一个事件和一个事件处理程序。事件处理程序通过多种方式注册和注销 据我所知,以下两种表达的结果是相同的:C# 作为参数传递给方法的事件处理程序不会取消注册,c#,events,delegates,C#,Events,Delegates,我用C#(附在下面)编写了一个非常简单的控制台测试应用程序,其中包含一个事件和一个事件处理程序。事件处理程序通过多种方式注册和注销 据我所知,以下两种表达的结果是相同的: Event1 += handler; Event1 += new TestEventHandler(handler); 现在,我主要希望这两个表达式也是如此: Event1 -= handler; Event1 -= new TestEventHandler(handler); 在这两种情况下,我都依赖于处理程序未注册。但
Event1 += handler;
Event1 += new TestEventHandler(handler);
现在,我主要希望这两个表达式也是如此:
Event1 -= handler;
Event1 -= new TestEventHandler(handler);
在这两种情况下,我都依赖于处理程序未注册。但似乎有一点不同:只是不同,当我将事件处理程序作为参数传递给一个方法时,,当方法中的代码使用-=new TestEventHandler(处理程序)
实现时,处理程序不会被取消注册。但是,如果代码直接使用-=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.
Event1-=newTestEventHandler(处理程序)
创建TestEventHandler的新实例。它在功能上可能与您第一次附加事件时创建的TestEventHandler相同,但它在内存中是不同的对象,不会具有相同的引用 因此,了解委托实际上是什么很重要。在引擎盖下,委托是指向方法和对象的指针。编写代码newtestenthandler(TestHandler)
时,您正在创建一个委托,该委托有一个指向TestHandler
的指针,并使用this
作为对象的引用
编写newtesteventhandler(handler)
时,您正在创建一个委托,其方法是调用handler
,对象是handler
代理的Equals
方法比较方法指针和它们各自存储的对象
引用是否相等
我们现在可以编写一个简短的测试用例来检查这个问题:
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
引用,所以它们“相等”<代码>一个
和三个
另一方面包装不同的方法指针和对象
引用,因此它们“不相等”。由于它们不相等,使用three
取消订阅不会从事件中删除one
他在每一次测试中创建新的代表引用,而不仅仅是最后一次。这不会改变我的答案。当他尝试运行我提到的代码行时,它创建一个TestEventHandler的新实例,然后尝试从事件中删除新实例,我不知道你为什么否决了正确答案:)你的答案不正确。学员不使用对自己的引用作为身份证明;它们覆盖它以执行基于值的比较。此外,正如我所说,他用来删除处理程序的每个委托都有一个不同于用来添加处理程序的委托的引用。最后一个例子在这方面没有什么不同。如果这个答案是真的,那么4个处理程序中没有一个被删除,而不是4个处理程序中的3个被删除。他在UnregisterHandler2中使用传递的处理程序,该处理程序通过值而不是引用传递,因此同一方法中的新TestEventHandler(处理程序)会创建一个TestEventHandler,其行为与前面相同,但具有不同的引用,因此当他试图删除事件处理程序时,它会尝试删除一个不存在的处理程序(因为它从未被添加)。本质上,他正在调用TestEventHandler构造函数以从eventhandler中删除。请参阅:两个TestEventHandler
实例将具有不同的引用,这是正确的。您错了,这就是代码行为如此的原因。委托重写对象。Equals
以对det执行基于值的比较代表有两种不同的引用,这与他们为什么“不相等”无关.正如我所说,这里的四个案例中的每一个都涉及到使用一个引用不同于用于订阅的委托来取消订阅;每一个案例,但只有最后一个不起作用。令人痛苦的问题。但是,是的,放弃new DelegateType(methodgroup)
等同于new DelegateType(DelegateType obj)的想法
。后者创建了一个委托对象,指向一个动态生成的存根,该存根调用obj
。实际上,您调用了两次。事件不会被取消订阅,因为这些存根的地址不匹配。我想他们没有提供逻辑操作,因为它太昂贵,难以实现向下