检查事件处理程序是否为null时,是否基于每个线程完成?
确保有人正在听这个事件就像这样:
EventSeven += new DivBySevenHandler(dbsl.ShowOnScreen);
如果我在上面检查null的模式之后添加代码,那么为什么我需要一个空检查(从这个站点获取的代码).我错过了什么?
此外,事件和GC的规则是什么?
问题是如果没有人订阅该事件,它就是null.并且你无法调用null.跳过三种方法:
检查为null(见下文)
添加"无所事事"处理程序: public event EventHandler MyEvent = delegate {};
使用扩展方法(见下文)
当空检查,是线程安全的,你必须在理论上首先捕捉委托引用(如果它的检查和调用不同的):
protected virtual void OnMyEvent() { EventHandler handler = MyEvent; if(handler != null) handler(this, EventArgs.Empty); }
扩展方法具有不寻常的属性,它们可以在null实例上调用...
public static void SafeInvoke(this EventHandler handler, object sender) { if (handler != null) handler(sender, EventArgs.Empty); } public static void SafeInvoke(this EventHandler handler, object sender, T args) where T : EventArgs { if (handler != null) handler(sender, args); }
然后你可以打电话:
MyEvent.SafeInvoke(this);
并且它是零安全的(通过检查)和线程安全的(通过仅读取一次引用).
我真的不清楚你的意思我害怕,但如果委托的可能性为null,你需要在每个线程上单独检查.通常你会这样做:
public void OnSeven() { DivBySevenHandler handler = EventSeven; if (handler != null) { handler(...); } }
这可以确保即使您EventSeven
在课程期间的更改OnSeven()
无法获得NullReferenceException
.
但是你是对的,如果你肯定有一个订阅的处理程序,你不需要空检查.这可以通过"无操作"处理程序在C#2中轻松完成:
public event DivBySevenHandler EventSeven = delegate {};
另一方面,如果您可以从各种线程获得订阅,您可能需要某种锁定以确保您拥有"最新"处理程序集.我在我的线程教程中有一个例子可以提供帮助 - 尽管我通常建议尽量避免要求它.
在垃圾收集方面,事件发布者最终会引用事件订阅者(即处理程序的目标).如果发布者的寿命比订阅者长,这只是一个问题.
我想附加一些关于C#6.0-Syntax的简短信息:
现在可以替换这个:
var handler = EventSeven; if (handler != null) handler.Invoke(this, EventArgs.Empty);
有了这个:
handler?.Invoke(this, EventArgs.Empty);
protected virtual void OnMyEvent() { EventHandler handler = MyEvent; handler?.Invoke(this, EventArgs.Empty); }
下到单线:
protected virtual void OnMyEvent() => MyEvent?.Invoke(this, EventArgs.Empty);