我正在阅读Essential C#3.0书籍,我想知道这是否是检查代表是否为null的好方法?:
class Thermostat
{
public delegate void TemperatureChangeHandler ( float newTemperature );
public TemperatureChangeHandler OnTemperatureChange { get; set; }
float currentTemperature;
public float CurrentTemperature
{
get { return this.currentTemperature; }
set
{
if ( currentTemperature != value )
{
currentTemperature = value;
TemperatureChangeHandler handler = OnTemperatureChange;
if ( handler != null )
{
handler ( value );
}
}
}
}
}
如果类型是不可变的,解决方案是否会更改?我认为可能具有不变性,你不会遇到这个线程问题。
答案 0 :(得分:11)
对此进行了很多讨论。
简而言之:即使执行此复制/检查null /执行步骤,也无法保证处理程序有效。
问题是,如果OnTemperatureChange在您复制它的时间和执行复制的时间之间取消注册,那么您可能无论如何都不希望执行该侦听器。
您也可以这样做:
if (OnTemperatureChange != null )
{
OnTemperatureChange ( value );
}
并处理空引用异常。
我有时会添加一个不执行任何操作的默认处理程序,只是为了防止空引用异常,但这会非常严重地增加性能影响,尤其是在没有注册其他处理程序的情况下。
我尊重Eric Lippert。
我的原始回复确实提到了使用默认处理程序,但我并不建议使用临时变量,我现在也认为这是一个很好的做法。
答案 1 :(得分:4)
建议使用a reason代码,而不是C. Ross的版本。但是,约翰也是对的,如果事件同时未注册,还有另一个问题。我链接的博客建议处理程序确保即使在未注册后也可以调用它们。
答案 2 :(得分:4)
首先,您实际上并未发布事件 - 所以目前您的代码“存在风险”,人们会完全搞砸它。它应该是:
public event TemperatureChangeHandler CurrentTemperatureChanged;
名称“CurrentTemperatureChanged”对于数据绑定很重要(运行时使用的约定 - 给定属性Foo,它将查找FooChanged)。 然而,IMO应该只是常规EventHandler
。数据绑定将查找EventHandler
,但更重要的是:如果订阅者无法通过查看obj.CurrentTemperature
来获取任何信息,您实际上并未提供任何信息。
我会在TemperatureChangeHandler
方面给出其余的答案,但我会鼓励你(再次)切换到EventHandler
:
public event EventHandler CurrentTemperatureChanged;
方法:
TemperatureChangeHandler handler = CurrentTemperatureChanged;
if(handler != null) handler(value);
是合理的,但是(根据其他回复),呼叫者认为他们断开接触事件的风险很小。实际上不太可能。
另一种方法是扩展方法:
public static class TemperatureChangeExt {
public static void SafeInvoke(this TemperatureChangeHandler handler,
float newTemperature) {
if (handler != null) handler(newTemperature);
}
}
然后在课堂上你可以使用:
if (currentTemperature != value) {
currentTemperature = value;
CurrentTemperatureChanged.SafeInvoke(value);
}
答案 3 :(得分:4)
使用问号进行条件访问:
OnTemperatureChange?.Invoke();
答案 4 :(得分:3)
如果Thermostat类不需要线程安全,那么上面的代码是正确的 - 只要只有一个线程访问Thermostat的实例,OnTemperatureChange就无法在null测试之间取消注册和对事件的召唤。
如果您需要使Thermostat线程安全,那么您可能需要查看以下文章(对我而言,看起来很好看):
http://www.yoda.arachsys.com/csharp/events.html
对于记录,建议您开发类不是线程安全的,除非明确需要线程安全,因为它会显着增加代码的复杂性。
答案 5 :(得分:1)
我只是看到一些可以完成的重构但是看起来不错......
class Thermostat
{
public delegate void TemperatureChangeHandler ( float newTemperature );
public TemperatureChangeHandler OnTemperatureChange { get; set; }
float currentTemperature;
public float CurrentTemperature
{
get { return this.currentTemperature; }
set
{
if (currentTemperature != value)
{
currentTemperature = value;
if (this.OnTemperatureChange != null )
{
this.OnTemperatureChange.Invoke( value );
}
}
}
}
}