首选:可为空 .HasValue或Nullable !=空吗?

我一直使用Nullable<>.HasValue由于我喜欢这种语义。 可是,最近我正在开发其余人的现有代码库,其中他们专门使用Nullable<> != nullgit

是否有理由优先使用一个,仍是纯粹是偏心? github

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

dom

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

#1楼

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


#2楼

我经过使用不一样的方法将值分配给可为null的int进行了一些研究。 这是我作各类事情时发生的事情。 应该澄清发生了什么。 请记住: Nullable<something>仍是速记的something? 是一种结构,编译器彷佛为此进行了大量工做,让咱们将null看成类使用。
以下所示, SomeNullable == nullSomeNullable.HasValue将始终返回预期的true或false。 尽管下面没有演示, SomeNullable == 3也有效(假设SomeNullable是int? )。
虽然SomeNullable.Value会让咱们运行时错误,若是咱们分配nullSomeNullable 。 实际上,因为重载运算符,重载object.Equals(obj)方法以及编译器优化和猴子业务的结合,这是惟一可能致使可为空的问题引发咱们问题的状况。 性能

这是我运行的一些代码的描述,以及它在标签中产生的输出: 测试

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);初始化int? val = new int?(null); int? val = new int?(null); ,若是将null传递给构造函数,则会产生COMPILE时间错误,由于可为空的对象的VALUE不可为空。 只有包装对象自己能够等于null。 spa

一样,咱们将从如下位置得到编译时错误: code

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

更不用说val.Value仍是一个只读属性,这意味着咱们甚至不能使用如下内容: 对象

val.Value = 3;

可是一样,多态重载的隐式转换运算符使咱们可以:

val = 3;

只要它能正常工做,就不用担忧杂乱的东西。 :)


#3楼

在VB.Net中。 当可使用“ .HasValue”时,请勿使用“ IsNot Nothing”。 我刚刚解决了“操做可能破坏运行时的稳定性”的中等信任错误,方法是将“ IsNot Nothing”替换为“ .HasValue”。 我真的不明白为何,可是编译器中发生的事情有所不一样。 我认为C#中的“!= null”可能有相同的问题。


#4楼

通常答案和经验法则:若是您有一个选择(例如,编写自定义序列化程序),能够在与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 。 人们说建议“因为始终如一的想法而首选HasValue”是无关的和无用的。 您能够预测其性能吗?

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

PPS人们继续CheckNullableGenericImpl但没有人试图预测CheckNullableGenericImpl性能。 并且那里的编译器不会帮助您用HasValue替换!=null 。 若是您对性能感兴趣,则应直接使用HasValue


#5楼

若是您使用linq并但愿使代码简短,我建议始终使用!=null

这就是为何:

假设咱们有一些带有空的双变量SomeDouble Foo

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

若是咱们想在代码中的某个地方从Foo集合中获取全部具备非null SomeDouble值的Foo(假设该集合中的某些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
}

在这种状况下,我建议老是选择较短的

相关文章
相关标签/搜索