我刚刚在程序中遇到一个错误,我正在编写一个异常,指出“对象引用必须设置为对象的实例”。经过调查,我发现在尝试触发事件时抛出了这个异常,但事件没有添加任何委托方法。
我想检查一下我的理解是否正确,作为开发人员,如果没有先检查事件是否等于 null ,就不应该触发事件?例如:
if (this.MyEventThatIWantToFire != null)
{
this.MyEventThatIWantToFire();
}
提前感谢您的建议/想法。
答案 0 :(得分:10)
您展示的模式在多线程环境中被破坏。在测试之后但在调用之前,MyEventThatIWantToFire
可能变为null。这是一种更安全的方法:
EventHandler handler = MyEventThatIWantToFire;
if (handler != null)
{
handler(...);
}
请注意,除非您使用某种类型的内存屏障,否则无法保证您会看到最新的订阅者,甚至无视明显的竞争条件。
但是,是的,除非你知道它将是非空的,否则你需要执行检查或使用帮助方法为你做检查。
确保总有订户的一种方法是自己添加无操作用户,例如
public event EventHandler MyEventThatIWantToFire = delegate {};
当然,事件不会具有来使用简单的委托字段来实现。例如,您可以拥有由List<EventHandler>
支持的事件,而不是使用空列表开始。那会很不寻常。
事件本身永远不会为空,因为事件本身只是一对方法(添加/删除)。有关详细信息,请参阅我的article about events and delegates。
答案 1 :(得分:4)
你不应该这样做 - 如果有人在if和对事件的调用之间移除一个监听器,你会得到一个例外。最好将它与局部变量一起使用:
protected void RaiseEvent()
{
var handler = Event;
if(handler != null)
handler(this, EventArgs.Empty);
}
当然,这样做的缺点是可能会在创建局部变量和调用处理程序之间调用已经删除的侦听器。
答案 2 :(得分:3)
是的,你应该检查它是否为null,但你这样做会导致竞争条件。如果有人在您的“if”和您的事件加注之间取消订阅最后一位代表,则该事件可能最终为null。接受的方法是:
var temp = this.MyEventThatIWantToFire;
if (temp != null) {
this.temp(this, EventArgs.Empty);
}
或者,通过声明您的事件,确保调用列表中始终至少有一个委托:
public event EventHandler MyEventThatIWantToFire = delegate {};
有意义吗?
答案 3 :(得分:1)
是的,您的理解是正确的。您可以在调用之前检查委托的值。
大多数代表 - 哪些事件是 - 是“多播”代表。这意味着委托可以管理激活时将调用的一个或多个方法的列表。
当委托评估为null
值时,没有注册方法;因此,没有什么可以称呼的。如果它评估的值不是null
,则至少注册一个方法。调用委托是调用它已注册的所有方法的指令。这些方法无法保证顺序调用。
使用加法赋值运算符(+=
)或加法运算符(+
)将方法添加到委托的方法列表中。使用减法赋值运算符(-=
)或减法运算符(-
)从委托的方法列表中删除方法。
另请注意,调用方法是从调用者的线程执行的。如果您使用事件来更新用户界面控件,这一点很重要。在这种情况下,使用您控件的InvokeRequired
属性可以优雅地处理跨线程调用(使用Invoke()
或BeginInvoke()
)。
答案 4 :(得分:0)
如果事件没有委托,则在C#中为是,它将为空,因此您必须检查
答案 5 :(得分:0)
是的,您必须检查事件是否为空。大多数情况下,您会遇到执行检查和调用事件的受保护方法,甚至构造事件参数。像这样:
public event EventHandler Foo;
protected void OnFoo() {
if (Foo == null)
return;
Foo(this, new EventArgs());
}
此方法还允许派生类调用(或阻止调用)事件。