首選:可爲空 .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
}

在這種狀況下,我建議老是選擇較短的

相關文章
相關標籤/搜索