C#事件,那个null的东西是什么?
我无法真正理解在提升事件时这个空测试的东西是什么。
说我有这个代码。
class ballClass { public event EventHandler BallInPlay; public void onHit() { if (BallInPlay != null) { BallInPlay(this, new EventArgs()); } else { MessageBox.Show("null!"); } } }
而且我想在触发onHit()方法时引发BallInPlay。
现在它告诉我BallInPlay是null。 我应该怎样或者用什么“填充”它才能起作用?
谢谢!
您需要使用处理程序订阅该事件。 例如:
BallClass ball = new BallClass(); ball.BallInPlay += BallInPlayHandler; // Now when ball.OnHit is called for whatever reason, BallInPlayHandler // will get called ... private void BallInPlayHandler(Object sender, EventArgs e) { // React to the event here }
有关更多信息,您可能需要阅读有关事件和委托的文章 。
请注意,我已经修复了上面的BallClass
和OnHit
– 最好使用标准的.NET命名约定来使代码更好地适应它周围的代码,并使其对其他人更具可读性。
有一点需要注意:您目前所获得的无效检查不是线程安全的。 最后一个订阅者可以在 if
但在调用之前取消订阅。 线程安全的版本是:
public void OnHit() { EventHandler handler = BallInPlay; if (handler != null) { handler(this, new EventArgs()); } else { MessageBox.Show("null!"); } }
这不能保证使用最新的订阅者(因为没有涉及内存障碍),但保证不会因竞争条件而抛出NullReferenceException。
这里的事情是,如果注册零监听器来监听此事件被引发,则该事件为空。 如果一个或多个听众被注册,它将具有一个值。
这意味着如果注册了零侦听器,并且您尝试在不执行空检查的情况下引发事件,则程序将抛出异常。
null测试是因为如果NOONE正在侦听事件,那么你swuold调用的事件对象实际上是null。 因此,没有订阅者,您将获得空指针exception。
如果没有为事件分配事件处理程序,则事件为null。 将事件视为List,当列表中没有项目时,事件的值变为null
。
在没有为其分配事件处理程序时尝试调用事件将引发NullReferenceException。
空检查可防止NullReferenceException。
如果你想让BallInPlay工作,你需要添加一个EventHandler
。
你的代码将是这样的:
BallInPlay += new EventHandler(YourFunctionNameGoesHere);
空测试用于确定是否有任何内容正在侦听事件。 一旦某事附加了事件处理程序,BallInPlay将不再为null
BallInPlay(this, new EventArgs());
一旦您意识到上面的代码行实际编译好,就好像您已经编写了以下代码,那么在触发事件之前进行null
检查的原因就会变得很明显:
BallInPlay.Invoke(this, new EventArgs()); // ^^^^^^^
众所周知,在null
引用上调用方法是个坏主意。 这就是为什么你需要首先检查BallInPlay != null
。
其他建议:
-
使用
EventArgs.Empty
而不是new EventArgs()
。 -
如果您按如下方式初始化
BallInPlay
,则可以绕过检查null
:public event EventHandler BallInPlay
= delegate { }
;
这是有效的,因为现在总是有一个订阅此事件的“空”处理程序方法。 因此,您可以保证在初始化后事件委托不再为
null
。 (有些人可能认为这效率稍低。)您始终可以使用
BallInPlay
事件中的+=
或-=
运算符订阅或取消订阅事件处理程序; 例如:ball.BallInPlay += (sender, e) => { /* react to the triggered event */ };
-
我认为你的
onHit
(或者更确切地说,如果我们遵守.NET世界中常见的约定,OnHit
) 方法不应该是公开的 ,因为这反映了事件的整个目的。 事件实际上是委托,但有一些额外的限制。 一个这样的限制是只有定义事件的类才能调用(“触发”)它。 另一方面,代表可以被任何人援引。 因此,如果你将onHit
公开,从而允许任何人触发你的事件,你也可以将BallInPlay
定义为委托,即没有event
关键字。 -
onHit
事件触发器方法的线程安全实现如下所示。 请注意,在方法内部使用事件处理程序委托的本地副本。 这是为了确保在检查null
和调用之间不会修改事件处理程序(例如,通过另一个线程)。
protected virtual void OnHit() { EventHandler handler = BallInPlay; // use a local copy of the event if (handler != null) // for better thread-safety! { handler(this, EventArgs.Empty); } }
BallInPlay += new EventHandler(myFunc); public void myFunc(object sender, EventArgs e) { MessageBox.Show("Woho \o/"); }