C# 检查委托是否为空

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/972932/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 04:34:50  来源:igfitidea点击:

Checking delegates for null

c#.netdelegates

提问by Joan Venge

I was reading the Essential C# 3.0 book and am wondering if this is a good way to check delegates for null?:

我正在阅读 Essential C# 3.0 书,想知道这是否是检查委托是否为空的好方法?:

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 );
                }
            }
        }
    }
}

Does the solution changes if the type is immutable? I figured maybe with immutability you wouldn't run into this threading problem.

如果类型是不可变的,解决方案是否会改变?我想也许通过不变性,你不会遇到这个线程问题。

采纳答案by John Weldon

Original (somewhat inaccurate) Response:

原始(有点不准确)回复:

There has been much discussion on this.

对此已经有很多讨论。

In short: you can't guarantee that the handler will be valid even by doing this copy/check for null/ execute step.

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

The problem is, if OnTemperatureChange is unregistered between the time you copy it, and the time you execute the copy, then it's probably true that you don't want the listener to be executed anyway.

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

You may as well just do:

你也可以这样做:

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

And handle a null reference exception.

并处理空引用异常。

I sometimes add a default handler that does nothing, just to prevent the null reference exception, but that adds performance impact quite seriously, especially in the case where there is no other handler registered.

我有时会添加一个什么都不做的默认处理程序,只是为了防止空引用异常,但这会严重影响性能,尤其是在没有注册其他处理程序的情况下。

Update 2014-07-10:

2014-07-10 更新:

I defer to Eric Lippert.

我尊重Eric Lippert

My original response did allude to using default handlers, but I didn't recommend using a temp variable, which I now agree as good practice also, per the article.

我最初的回答确实暗示使用默认处理程序,但我不建议使用临时变量,根据文章,我现在也同意这是一种很好的做法。

回答by Matthew Flaschen

There is a reasonthe code you've given is recommended over C. Ross's version. However, John is also right that there is still another problem if an event is unregistered in the meanwhile. The blog I linked recommends that the handler ensure they can be called even after being unregistered.

推荐您提供的代码而不是 C. Ross 的版本是有原因的。但是,John 也是对的,如果同时未注册事件,则还有另一个问题。我链接的博客建议处理程序确保即使在未注册后也可以调用它们。

回答by bytebender

I just see a bit of refactoring that could be done but otherwise it looks good...

我只是看到一些可以完成的重构,但否则看起来不错......

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 );
                        }
                }
        }
    }
}

回答by Justin

If the Thermostat class doesn't need to be thread safe then yes the above code is fine - as long as there is only one thread accessing that instance of Thermostat there is no way for OnTemperatureChange to become unregistered between the test for null and the call to the event.

如果 Thermostat 类不需要是线程安全的,那么是的,上面的代码很好 - 只要只有一个线程访问 Thermostat 的该实例, OnTemperatureChange 就无法在 null 测试和调用之间取消注册到活动。

If you need to make Thermostat thread safe then you might want to take a look at the following article (new to me, looks like a good read):

如果您需要使 Thermostat 线程安全,那么您可能需要查看以下文章(对我来说是新的,看起来不错):

http://www.yoda.arachsys.com/csharp/events.html

http://www.yoda.arachsys.com/csharp/events.html

For the record, the recommendation is that you develop your classes not to be thread safe unless thread safety is explicitly needed as it can significantly increase the complexity of your code.

作为记录,建议您将类开发为线程安全的,除非明确需要线程安全,因为它会显着增加代码的复杂性。

回答by Marc Gravell

First, you aren't actually publishing an event- so at the moment, your code is "at risk" of people messing it up completely. It should be:

首先,您实际上并没有发布事件——所以目前,您的代码“有可能”被人们完全搞砸。它应该是:

public event TemperatureChangeHandler CurrentTemperatureChanged;

The name "CurrentTemperatureChanged" is important for data-binding (there is a convention that the runtime uses - given a property Foo, it will look for FooChanged). However, IMO this should just be regular EventHandler. Data-binding will look for EventHandler, but more importantly: you aren't actually giving any information in the event that the subscriber can't already get just by looking at obj.CurrentTemperature.

名称“CurrentTemperatureChanged”对于数据绑定很重要(运行时使用一个约定——给定属性 Foo,它将查找 FooChanged)。但是,IMO 这应该只是常规的EventHandler。数据绑定将查找EventHandler,但更重要的是:如果订阅者仅通过查看obj.CurrentTemperature.

I'll give the rest of the answer in terms of TemperatureChangeHandler, but I would encourage you (again) to switch to EventHandler:

我将给出其余的答案TemperatureChangeHandler,但我会鼓励您(再次)切换到EventHandler

public event EventHandler CurrentTemperatureChanged;

The approach:

该方法:

TemperatureChangeHandler handler = CurrentTemperatureChanged;
if(handler != null) handler(value);

is reasonable, but (as per other replies) there is a slim risk of callers that think they disconnected getting the event. Unlikely in reality.

是合理的,但是(根据其他回复)呼叫者认为他们断开连接的风险很小。现实中不太可能。

Another approach is an extension method:

另一种方法是扩展方法:

public static class TemperatureChangeExt {
    public static void SafeInvoke(this TemperatureChangeHandler handler,
             float newTemperature) {
        if (handler != null) handler(newTemperature);
    }
}

Then in your class you can just use:

然后在你的课堂上你可以使用:

        if (currentTemperature != value) {
            currentTemperature = value;
            CurrentTemperatureChanged.SafeInvoke(value);
        }

回答by Luca Ziegler

Use a question mark for a conditional access:

使用问号进行条件访问:

OnTemperatureChange?.Invoke();

OnTemperatureChange?.Invoke();