首页 文章

首选:Nullable <> . HasValue或Nullable <>!= null?

提问于
浏览
363

我总是使用(a) Nullable<>.HasValue 因为我喜欢语义 . 但是,最近我正在研究其他人的现有代码库,他们专门使用(b) Nullable<> != null 代替 . 是否有理由使用一个而不是另一个,还是纯粹的偏好?

(一个)

int? a;
if (a.HasValue)
    ...

(b)中

int? b;
if (b != null)
    ...

6 回答

  • 19

    编译器将空比较替换为对 HasValue 的调用,因此没有真正的区别 . 只要做一个对您和您的同事更具可读性/更有意义的事情 .

  • 14

    我更喜欢 (a != null) ,以便语法与引用类型匹配 .

  • 41

    我通过使用不同的方法将值赋给可空int来对此进行了一些研究 . 这是我做各种事情时发生的事情 . 应该澄清发生了什么 . 请记住: Nullable<something> 或简写 something? 是一个结构,编译器似乎正在做很多工作,让我们使用null,就像它是一个类一样 .
    正如您将在下面看到的那样, SomeNullable == nullSomeNullable.HasValue 将始终返回预期的true或false . 虽然下面没有说明, SomeNullable == 3 也是有效的(假设SomeNullable是 int? ) .
    如果我们将 null 分配给 SomeNullableSomeNullable.Value 会给我们带来运行时错误 . 事实上,这是唯一一个由于重载运算符,重载 object.Equals(obj) 方法以及编译器优化和猴子业务的组合,nullables可能导致我们出现问题的情况 .

    以下是我运行的一些代码的说明,以及它在标签中生成的输出:

    int? val = null;
    lbl_Val.Text = val.ToString(); //Produced an empty string.
    lbl_ValVal.Text = val.Value.ToString(); //Produced a runtime error. ("Nullable object must have a value.")
    lbl_ValEqNull.Text = (val == null).ToString(); //Produced "True" (without the quotes)
    lbl_ValNEqNull.Text = (val != null).ToString(); //Produced "False"
    lbl_ValHasVal.Text = val.HasValue.ToString(); //Produced "False"
    lbl_NValHasVal.Text = (!(val.HasValue)).ToString(); //Produced "True"
    lbl_ValValEqNull.Text = (val.Value == null).ToString(); //Produced a runtime error. ("Nullable object must have a value.")
    lbl_ValValNEqNull.Text = (val.Value != null).ToString(); //Produced a runtime error. ("Nullable object must have a value.")
    

    好的,让我们尝试下一个初始化方法:

    int? val = new int?();
    lbl_Val.Text = val.ToString(); //Produced an empty string.
    lbl_ValVal.Text = val.Value.ToString(); //Produced a runtime error. ("Nullable object must have a value.")
    lbl_ValEqNull.Text = (val == null).ToString(); //Produced "True" (without the quotes)
    lbl_ValNEqNull.Text = (val != null).ToString(); //Produced "False"
    lbl_ValHasVal.Text = val.HasValue.ToString(); //Produced "False"
    lbl_NValHasVal.Text = (!(val.HasValue)).ToString(); //Produced "True"
    lbl_ValValEqNull.Text = (val.Value == null).ToString(); //Produced a runtime error. ("Nullable object must have a value.")
    lbl_ValValNEqNull.Text = (val.Value != null).ToString(); //Produced a runtime error. ("Nullable object must have a value.")
    

    和以前一样 . 请记住,使用 int? val = new int?(null); 初始化,并将null传递给构造函数,会产生COMPILE时间错误,因为可空对象的VALUE不可为空 . 只有包装器对象本身才能等于null .

    同样,我们会得到一个编译时错误:

    int? val = new int?();
    val.Value = null;
    

    更不用说 val.Value 无论如何都是一个只读属性,这意味着我们甚至不能使用类似的东西:

    val.Value = 3;
    

    但同样,多态重载隐式转换运算符让我们做:

    val = 3;
    

    不管怎么说,只要它能正常工作,就不用担心多元化的问题了吗? :)

  • 1

    在VB.Net中 . 当你可以使用“.HasValue”时,不要使用“IsNot Nothing” . 我刚刚在一个地方用“.HasValue”替换了“IsNot Nothing”,解决了“操作可能使运行时不稳定”的中等信任错误 . 我真的不明白为什么,但在编译器中发生了不同的事情 . 我认为C#中的“!= null”可能会有同样的问题 .

  • -6

    如果您使用linq并希望保持代码简短,我建议始终使用 !=null

    这就是为什么:

    假设我们有一个 Foonullable double 变量 SomeDouble

    public class Foo
    {
        public double? SomeDouble;
        //some other properties
    }
    

    如果在我们的代码中的某个地方,我们希望从Foo的集合中获取所有Foo的非空SomeDouble值(假设集合中的某些foos也可以为null),我们最终至少有三种方式来编写我们的函数(如果我们使用C#6):

    public IEnumerable<Foo> GetNonNullFoosWithSomeDoubleValues(IEnumerable<Foo> foos)
    {
         return foos.Where(foo => foo?.SomeDouble != null);
         return foos.Where(foo=>foo?.SomeDouble.HasValue); // compile time error
         return foos.Where(foo=>foo?.SomeDouble.HasValue == true); 
         return foos.Where(foo=>foo != null && foo.SomeDouble.HasValue); //if we don't use C#6
    }
    

    在这种情况下,我建议总是选择较短的一个

  • 394

    一般答案和经验法则:如果您有一个选项(例如编写自定义序列化程序)来处理不同于 object 的管道中的Nullable - 并使用它们的特定属性 - 请执行此操作并使用Nullable特定属性 . 所以从一致的思维角度来看 HasValue 应该是首选 . 一致的思考可以帮助你编写更好的代码,不要在细节上花太多时间 . 例如 . 第二种方法将会多次有效(主要是因为编译器内联和装箱,但仍然数字很有表现力):

    public static bool CheckObjectImpl(object o)
    {
        return o != null;
    }
    
    public static bool CheckNullableImpl<T>(T? o) where T: struct
    {
        return o.HasValue;
    }
    

    基准测试:

    BenchmarkDotNet=v0.10.5, OS=Windows 10.0.14393
    Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
    Frequency=3233539 Hz, Resolution=309.2587 ns, Timer=TSC
      [Host] : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1648.0
      Clr    : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1648.0
      Core   : .NET Core 4.6.25009.03, 64bit RyuJIT
    
    
            Method |  Job | Runtime |       Mean |     Error |    StdDev |        Min |        Max |     Median | Rank |  Gen 0 | Allocated |
    -------------- |----- |-------- |-----------:|----------:|----------:|-----------:|-----------:|-----------:|-----:|-------:|----------:|
       CheckObject |  Clr |     Clr | 80.6416 ns | 1.1983 ns | 1.0622 ns | 79.5528 ns | 83.0417 ns | 80.1797 ns |    3 | 0.0060 |      24 B |
     CheckNullable |  Clr |     Clr |  0.0029 ns | 0.0088 ns | 0.0082 ns |  0.0000 ns |  0.0315 ns |  0.0000 ns |    1 |      - |       0 B |
       CheckObject | Core |    Core | 77.2614 ns | 0.5703 ns | 0.4763 ns | 76.4205 ns | 77.9400 ns | 77.3586 ns |    2 | 0.0060 |      24 B |
     CheckNullable | Core |    Core |  0.0007 ns | 0.0021 ns | 0.0016 ns |  0.0000 ns |  0.0054 ns |  0.0000 ns |    1 |      - |       0 B |
    

    基准代码:

    public class BenchmarkNullableCheck
    {
        static int? x = (new Random()).Next();
    
        public static bool CheckObjectImpl(object o)
        {
            return o != null;
        }
    
        public static bool CheckNullableImpl<T>(T? o) where T: struct
        {
            return o.HasValue;
        }
    
        [Benchmark]
        public bool CheckObject()
        {
            return CheckObjectImpl(x);
        }
    
        [Benchmark]
        public bool CheckNullable()
        {
            return CheckNullableImpl(x);
        }
    }
    

    使用了https://github.com/dotnet/BenchmarkDotNet

    PS . 人们说建议"prefer HasValue because of consistent thinking"没有关联和无用 . Can you predict the performance of this?

    public static bool CheckNullableGenericImpl<T>(T? t) where T: struct
    {
        return t != null;
    }
    

    PPS 人们继续减去,但没有人试图预测 CheckNullableGenericImpl 的表现 . 并且编译器不会帮助您用 HasValue 替换 !=null . 如果您对性能感兴趣,应直接使用 HasValue .

相关问题