有很多讨论
if (SomeEvent != null)
SomeEvent(this, null);
可能产生NullReference。
这
SomeEventHandler temp = SomeEvent;
if (temp != null)
temp(this, null);
稍微好一点,但编译器可以优化temp
变量,我们会回到问题。
然后解决这个问题应该是稳定的
SomeEventHandler temp = Interlocked.CompareExchange(ref SomeEvent, null, null)
if (temp!=null)
{
temp(this, e)
}
我可能会误解事件链的维护方式。 这是否意味着当链内的事件被取消订阅时,它的占位符变为空?!
让我们说SomeEvent = {eventHandler1,eventHandler2,eventHandler3}。
某些线程取消订阅SomeEvent中的eventHandler2。
不应该从第一个exerpt中SomeEvent(this, e)
只调用eventHandler1而不是eventHandler3吗?
为什么选择NullReference?
我可以理解,当没有订户存在时,SomeEvent为null。 但是,解决方案是值得怀疑的,因为如果订阅者取消订阅,那么我们就不应该强行解雇它。
我可能错过了一些关于事件的事情。请解释一下。
如果null只能出现没有剩余处理程序的结果, 比整个解决方案都值得怀疑,如果不是愚蠢的话。 因为我为什么要在取消订阅的对象上调用事件处理程序(现在可能正在处理并且处于不稳定状态)!
答案 0 :(得分:0)
为什么选择NullReference?
事件的后备存储是委托对象。代表是不可改变的。取消订阅事件会调用MulticastDelegate.RemoveImpl()来创建新对象。看看代码,注意当没有调用时它返回null。所以是的,SomeEvent可以在您忙于提升事件时变为null。经典的线程竞赛错误。保留副本可防止客户端代码使其为空。
如果不是愚蠢的话,整个解决方案是值得怀疑的
是。如果您需要此代码来执行预期的操作,则会出现问题。有了解决方法,事件订阅者将获得该事件,即使它在几纳秒之前取消订阅。另一个线程竞赛错误。
然而,重要的是谁是愚蠢的。取消订阅该事件的代码通常由其他人编写。始终是框架中事件的情况,在您自己的代码中并不常见。如果该客户端代码使用线程并且它没有正确锁定,那么您的代码会崩溃。不是愚蠢的代码。
您不希望该错误报告。
客户端代码可以处理竞争。它造成了它,它可以做些什么。你不能。