在设置为null之前检​​查是否为null?

时间:2017-02-23 13:21:19

标签: c# .net null

我们应该在将变量设置为null之前检​​查变量是否为空吗?

if (MyBills != null) 
{
    MyBills = null;
}

例如,在Java related question中,性能影响很小。 这是C#中的情况吗?其他影响?

测试

我已经创建了以下代码进行测试:

var watch = System.Diagnostics.Stopwatch.StartNew();

int iterations = int.MaxValue;
List<int> myBills= null;
for (var i = 0; i < iterations; i++)
{
    if (myBills!= null)
    {
        myBills = null;
    }
}
watch.Stop();
var elapsedMs = watch.ElapsedMilliseconds;
Console.WriteLine(elapsedMs);

rextester上使用if (myList != null)运行结果如下:

With check      Without check
988             941
938             1021
953             987
998             973
1004            1031

Average 
976.2           990.6

因此,即使在非受控环境中对其进行测试,性能影响也无关紧要。

3 个答案:

答案 0 :(得分:13)

不,没有多大用处。可能检查变量是否为null,与将其设置为null一次太多一样昂贵。

如果是属性,后面还有其他逻辑,那么之前测试它是有意义的,但这实际上应该是属性中逻辑的责任,而不是代码。< / p>

答案 1 :(得分:5)

除了property / setter参数之外,我在你提供的代码中看不出任何合乎逻辑的原因。

但是,如果要在将对象设置为null之前对该对象执行额外操作,则有意义:

if (MyBills != null)
{
     MyBills.Dispose();
     MyBills = null;
}

或执行一些其他操作,例如记录结果。

但是这两个例子都在你提供的示例代码之外。

因为人们似乎在质疑我的第一个例子的用例,所以这是一个。这样可以防止多次调用Dispose导致ObjectDisposedException

public class MyClass : IDisposable
{
    private SomeDisposableObject _disposableObject;

    //Some code

    public void Dispose()
    {
        if (_disposableObject != null)
        {
            _disposableObject.Dispose();
            _disposableObject = null;
        }
    }
}

我已经完成了一些时间安排并找到了以下结果:

  

valueClass with null check = 1361
  nullClass with null check = 1173
  valueClass没有空检查= 1208
  nullClass,无空检查= 1148

正如您在没有空检查的情况下所看到的那样,它稍微快一些,但不足以进行任何重大优化。此外,我在调试模式下从编译器执行了这些时序,并关闭了优化,因此它们不是100%准确/相关。

然而,当我在发布模式下运行时,在编译器外部启用了优化,结果非常接近,检查与否则更加可以忽略不计。

测试代码:

using System;

namespace NullCheckTest
{
    class Program
    {
        const int iterations = 10000000;

        static void Main(string[] args)
        {
            MyClass valueClass = new MyClass() { Value = 10 };
            MyClass nullClass = null;

            Console.WriteLine($"valueClass with null check = {TestNullCheck(valueClass)}");
            Console.WriteLine($"nullClass with null check = {TestNullCheck(nullClass)}");

            Console.WriteLine($"valueClass with no null check = {TestNoNullCheck(valueClass)}");
            Console.WriteLine($"nullClass with no null check = {TestNoNullCheck(nullClass)}");

            Console.ReadLine();
        }

        static long TestNullCheck(MyClass myClass)
        {
            MyClass initial = myClass;

            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            for (int i = 0; i < iterations; ++i)
            {
                sw.Start();

                if (myClass != null)
                {
                    myClass = null;
                }

                sw.Stop();

                myClass = initial;
            }

            return sw.ElapsedMilliseconds;
        }

        static long TestNoNullCheck(MyClass myClass)
        {
            MyClass initial = myClass;

            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            for (int i = 0; i < iterations; ++i)
            {
                sw.Start();

                myClass = null;

                sw.Stop();

                myClass = initial;
            }

            return sw.ElapsedMilliseconds;
        }
    }

    public class MyClass
    {
        public int Value { get; set; }
    }
}

答案 2 :(得分:-2)

回答这个问题:不,没有必要。

然而。作为一般经验法则,对象变量和属性永远不应为null,因为它会强制您在某处设置值时始终必须检查代码。

您应该做的是使用自定义类Maybe<>来表示可能未初始化对象变量或属性。

像这样:

public class Maybe<T> where T : class
{
    public readonly T Value { get; }

    public Maybe(T value)
    {
        _Value = value;
    }

    public bool HasValue => _Value != null;
    public bool HasNoValue => _Value == null;

    public static operation Maybe<T>(T value) => new Maybe(value);
}

然后你可以这样使用它:

public class Foo
{
    public Maybe<Bar> Bar { get; set; }
    public int GetBarCount()
    {
        return Bar.HasValue ? Bar.Count : 0;
    }
}

public class Bar
{
    public int Count { get; set; }
}

上述代码适用于以下所有示例:

Foo foo = new Foo();
foo.Bar = new Bar(); // Outputs 0 when GetBarCount is called
foo.Bar = new Bar { Count = 2 }; // Outputs 2 when GetBarCount is called
foo.Bar = new Maybe<Bar>(new Bar()); // Outputs 0 when GetBarCount is called
foo.Bar = new Maybe<Bar>(new Bar { Count = 2 }); // Outputs 2 when GetBarCount is called
foo.Bar = new Maybe<Bar>(null); // Outputs 0 when GetBarCount is called
foo.Bar = null;  // Outputs 0 when GetBarCount is called

请注意! - 使用Maybe<Bar>时,您明确标记Foo.Bar并不总是有值,并提供强有力的信号。但是,不用Maybe<Bar>标记它,表示它始终具有值。通过在正确的位置使用Maybe<>包装器,代码变得更容易使用,因为您不再需要对未包装的对象变量和属性进行 null 检查。