检查委托是否为null

时间:2009-06-09 23:01:18

标签: c# .net delegates

我正在阅读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 );
                }
            }
        }
    }
}

如果类型是不可变的,解决方案是否会更改?我认为可能具有不变性,你不会遇到这个线程问题。

6 个答案:

答案 0 :(得分:11)

原创(有些不准确)回复:

对此进行了很多讨论。

简而言之:即使执行此复制/检查null /执行步骤,也无法保证处理程序有效。

问题是,如果OnTemperatureChange在您复制它的时间和执行复制的时间之间取消注册,那么您可能无论如何都不希望执行该侦听器。

您也可以这样做:

if (OnTemperatureChange != null )
{
    OnTemperatureChange ( value );
}

并处理空引用异常。

我有时会添加一个不执行任何操作的默认处理程序,只是为了防止空引用异常,但这会非常严重地增加性能影响,尤其是在没有注册其他处理程序的情况下。

更新2014-07-10:

我尊重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 );
                        }
                }
        }
    }
}