Hangisi tercih edilir: Boş değer <T> .HasValue veya Boş değer <T>! = Boş?


437

Her zaman kullandım Nullable<>.HasValueçünkü anlambilimi sevdim. Ancak, son zamanlarda başka birinin mevcut kod tabanı üzerinde çalışıyordum Nullable<> != null.

Birini diğerinin üzerinde kullanmak için bir neden var mı, yoksa sadece tercih mi?

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

vs.

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

9
Benzer bir soru sordum ... bazı iyi cevaplar aldım: stackoverflow.com/questions/633286/…
nailitdown

3
Şahsen , HasValuekelimelerin sembollerden daha okunabilir olma eğiliminde olduğunu düşündüğüm için kullanırım . Her şey size kalmış ve mevcut tarzınıza uyan şey.
Jake Petroules

1
.HasValueT?dize gibi boş bırakılabilen bir türden ziyade türün tür olduğunu belirtmesi daha mantıklıdır .
user3791372

Yanıtlar:


476

Derleyici null karşılaştırmaları çağrıyla değiştirir HasValue, bu yüzden gerçek bir fark yoktur. Siz ve meslektaşlarınız için hangisi daha okunaklı / mantıklı olursa yapın.


86
Buna "hangisi daha tutarlı / mevcut bir kodlama stilini izliyor" eklerdim.
Josh Lee

20
Vay. Bu sözdizimsel şekerden nefret ediyorum. int? x = nullnullable örneğinin başvuru türü olduğu yanılsamasını verir. Ama gerçek şu ki Nullable <T> bir değer türüdür. Yapmam bir NullReferenceException alırdım hisseder: int? x = null; Use(x.HasValue).
KFL

11
@KFL Eğer sözdizimsel şeker sizi rahatsız ediyorsa Nullable<int>bunun yerine kullanın int?.
Cole Johnson

24
Bir uygulama oluşturmanın ilk aşamalarında, yalnızca bir süre sonra amacınız için uygun bir sınıfa ihtiyacınız olduğunu fark etmek için bazı verileri saklamak için bir boş değer türü kullanmanın yeterli olduğunu düşünebilirsiniz. Null ile karşılaştırmak için orijinal kodu yazdığınızda, HasValue () öğesine yapılan her çağrıyı null karşılaştırma ile aramanıza / değiştirmenize gerek kalmaz.
Anders

15
Null değeri null değerine ayarlayabilmekten veya Null değeri adı verilen null değeriyle karşılaştırmaktan şikayet etmek oldukça aptalca . Sorun şu ki, insanlar "referans türü" nü "boş" olabilir, ama bu kavramsal bir karışıklıktır. Gelecekteki C #, nullable referans türlerine sahip olacak.
Jim Balter

48

(a != null)Sözdiziminin referans türleriyle eşleşmesini tercih ederim .


11
Çünkü hangi oldukça, elbette, yanıltıcı Nullable<>olduğu değil bir başvuru türü.
Luaan

9
Evet, ancak gerçek null denetimi yaptığınız noktada çok az önemlidir.
cbp

31
Sadece kavramsal olarak karıştırılmış olan için yanıltıcıdır. İki farklı tür için tutarlı bir sözdizimi kullanmak, bunların aynı tür olduğu anlamına gelmez. C # null edilebilir referans türlerine sahiptir (tüm referans türleri şu anda null edilebilir, ancak gelecekte değişecektir) ve null edilebilir değer türlerine sahiptir. Null olabilecek tüm türler için tutarlı bir sözdizimi kullanmak mantıklıdır. Hiçbir şekilde nullable değer türlerinin referans türleri veya nullable referans türlerinin değer türleri olduğu anlamına gelmez.
Jim Balter

Ben tercih ederim HasValueçünkü daha okunabilir!= null
Konrad

Aynı kodu yazmanın farklı stillerini karıştırmazsanız, kodlama tutarlılığı daha okunabilir. Tüm yerler bir .HasValue özelliğine sahip olmadığından, tutarlılığı artırmak için! = Null kullanılmasını sağlar. Benim düşüncemde.
ColacX

21

Nullable int değer atamak için farklı yöntemler kullanarak bu konuda biraz araştırma yaptım. İşte çeşitli şeyler yaptığımda olan şey. Neler olduğunu netleştirmeli. Unutmayın: Nullable<something>ya da steno something?, derleyicinin sanki bir sınıfmış gibi kullanmamıza izin vermek için çok çalıştığı bir yapıdır.
Aşağıda göreceğiniz gibi SomeNullable == nullve SomeNullable.HasValueher zaman doğru veya yanlış beklenen dönecektir. Aşağıda gösterilmemesine rağmen SomeNullable == 3, geçerlidir (SomeNullable'ın bir olduğu varsayılırsa int?).
İken SomeNullable.Valuebize bir çalışma zamanı hatası alır biz atanmış ise nullhiç SomeNullable. Bu, aşırı yüklenmiş operatörlerin, aşırı yüklenmiş bir kombinasyon sayesinde nullablerin bize bir soruna neden olabileceği tek durumdurobject.Equals(obj) yöntemi ve derleyici optimizasyonu ve maymun işi.

İşte çalıştırdığım bazı kodların ve etiketlerde hangi çıktıların üretildiğinin açıklaması:

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.")

Tamam, bir sonraki başlatma yöntemini deneyelim:

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.")

Hepsi eskisi gibi. İle başlatılıyor unutmayın int? val = new int?(null);null nesnenin DEĞER null değil çünkü yapıcısı geçirilen null, bir DERLEME zaman hata oluşturdu olurdu. Null değerine eşit olan yalnızca sarıcı nesnenin kendisidir.

Aynı şekilde, derleme zamanı hatası alırız:

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

val.Valuezaten salt okunur bir özellik olduğundan bahsetmiyorum, yani şöyle bir şey bile kullanamayız:

val.Value = 3;

ancak yine, polimorföz aşırı yüklenmiş örtük dönüşüm operatörleri şunları yapmamıza izin verir:

val = 3;

Doğru çalıştığı sürece polisomthing whatchamacallits hakkında endişelenmenize gerek yok mu? :)


5
"Unutmayın: Nullable <something> ya da steno bir şey? Bir sınıf." Bu yanlış! Sıfırlanabilir <T> bir yapıdır. Null değerine göre true döndürmek için Eşittir ve == operatörünü aşırı yükler. Derleyici bu karşılaştırma için hiçbir süslü çalışma yapmaz.
andrewjs

1
@andrewjs - Bunun bir yapı olduğunu (sınıf değil) haklısınız, ancak == operatörünü aşırı yüklediği konusunda yanılıyorsunuz. Eğer yazarsanız Nullable<X>VisualStudio 2013 F12 içeri, bunu sadece ve dönüşümü overloads göreceksiniz Xve Equals(object other)yöntemi. Ancak, == operatör varsayılan olarak bu yöntemi kullandığını düşünüyorum, bu yüzden etkisi aynı. Aslında bir süredir bu gerçeğin cevabını güncellemek için anlam ifade ediyorum, ama tembel ve / veya meşgulüm. Bu yorumun şimdilik yapması gerekecek :)
Perrin Larson

Ildasm ile hızlı bir kontrol yaptım ve derleyici biraz sihir yapıyor haklısın; Nullable <T> nesnesini null ile karşılaştırmak aslında HasValue çağrısına dönüşür. İlginç!
andrewjs

3
@andrewjs Aslında, derleyici nullables'i optimize etmek için bir ton iş yapıyor. Örneğin, boş değer türüne bir değer atarsanız, gerçekte hiç boş değer olmaz (ör int? val = 42; val.GetType() == typeof(int).). Bu nedenle, null değeri null değerine eşit olabilecek bir yapı değil, aynı zamanda null değeri de yoktur! : D Aynı şekilde, boş değer içeren bir değeri işaretlediğinizde, boks yaparsınız int, değilsinizdir int?ve int?değer içermiyorsa, nullkutulanmış boş değer yerine elde edersiniz . Temel olarak, nullable'ı düzgün kullanmanın nadiren herhangi bir ek yükü olduğu anlamına gelir :)
Luaan

1
@JimBalter Gerçekten mi? Bu çok ilginç. Peki bellek profiler size bir sınıftaki boş alan hakkında ne söylüyor? C # 'da başka bir değer türünden miras alan bir değer türünü nasıl bildirirsiniz? .NET'in null edilebilir türüyle aynı davranan kendi null edilebilir türünüzü nasıl bildirirsiniz? Ne zamandan beri Nullbir tür .NET? CLR / C # belirtiminde bunun söylendiği yere işaret edebilir misiniz? Nullables CLR spesifikasyonunda iyi tanımlanmıştır, davranışları "soyutlamanın uygulanması" değildir - bu bir sözleşmedir . Ancak yapabileceğiniz en iyi şey ad hominem saldırıları ise, keyfinize bakın.
Luaan

13

VB.Net'te. ".HasValue" kullanabileceğiniz zaman "IsNot Nothing" kullanmayın. Ben sadece bir "Operasyon çalışma zamanı dengesini bozabilir" Çözüldü "IsNot Nothing" yerine ".HasValue" yerine Orta güven hatası. Nedenini gerçekten anlamıyorum, ancak derleyicide bir şeyler farklı oluyor. C # "! = Null" aynı sorunu olabilir varsayalım.


8
HasValueOkunabilirlik nedeniyle tercih ederim . IsNot Nothinggerçekten çirkin bir ifadedir (çifte olumsuzluk yüzünden).
Stefan Steinegger

12
@steffan "IsNot Nothing" iki kat olumsuzlama değil. "Hiçbir şey" negatif değildir, programlama alanı dışında bile ayrı bir miktardır. "Bu miktar hiçbir şey değil." dilbilgisi açısından, "Bu miktar sıfır değil" demekle tamamen aynıdır. ve ikisi de çift negatif değildir.
jmbpiano

5
Burada gerçeğin yokluğuna katılmamak istemiyorum, ama şimdi gel. Hiçbir şey açıkça, iyi, aşırı olumsuz değildir. Neden HasValue gibi olumlu ve net bir şey yazmıyorsunuz? Bu bir dilbilgisi testi değil, kodlama, anahtar hedefin netlik olduğu yer
Randy Gamage

3
jmbpiano: Bunun çifte olumsuzlama olmadığına katılıyorum, ama tek bir olumsuzlama ve bu neredeyse çirkin ve basit bir pozitif ifade kadar net değil.
Kaveh Hadjari

0

Linq kullanıyorsanız ve kodunuzu kısa tutmak istiyorsanız, her zaman kullanmanız önerilir !=null

İşte bu yüzden:

Düşünsüz çift değişkenli Foobir sınıfımız olduğunu düşünelimSomeDouble

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

Kodumuzda bir yerde tüm Foo'ları boş olmayan bir SomeDouble değeri olan bir Foo koleksiyonundan (koleksiyondaki bazı foosların da boş olabileceğini varsayarak) almak istiyorsak, fonksiyonumuzu yazmanın en az üç yolunu buluruz (eğer C # 6 kullanın):

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
}

Ve bu tür bir durumda daima daha kısa olanı tercih etmeyi öneririm


2
Evet, foo?.SomeDouble.HasValuederleme zamanı hatası (benim terminolojimdeki bir "atma" değil), çünkü türü bool?sadece değil bool. ( .WhereYöntem a istiyor Func<Foo, bool>.) (foo?.SomeDouble).HasValueTabii ki bu tip olduğundan , yapmasına izin verilir bool. İlk satırınız C # derleyicisi tarafından dahili olarak "en azından resmi olarak" çevrilir.
Jeppe Stig Nielsen

-6

Genel cevap ve genel kural: Nullable'ı farklı pipeline'da işlemek objectve belirli özelliklerini kullanmak için bir seçeneğiniz varsa (örneğin özel serileştiriciler yazmak) bunu yapın ve Nullable spesifik özelliklerini kullanın. Dolayısıyla tutarlı düşünme açısından HasValuetercih edilmelidir. Tutarlı düşünme, daha fazla kod yazmanıza yardımcı olabilir, ayrıntılarda çok fazla zaman harcamayın. Örneğin, ikinci yöntem birçok kez daha etkili olacaktır (çoğunlukla derleyiciler inlining ve boks nedeniyle, ancak yine de sayılar çok etkileyici):

public static bool CheckObjectImpl(object o)
{
    return o != null;
}

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

Deney testi:

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 |

Karşılaştırma kodu:

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 kullanıldı

PS . İnsanlar, tavsiyenin "tutarlı düşünme nedeniyle HasValue'yu tercih ettiğini" ilgili ve işe yaramaz olduğunu söylüyor. Bunun performansını tahmin edebilir misiniz?

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

PPS İnsanlar eksi devam ediyor ama kimse performansını tahmin etmeye çalışmıyor CheckNullableGenericImpl. Ve orada derleyici değiştirmekte yardımcı olmayacaktır !=nullile HasValue. HasValueperformansla ilgileniyorsanız doğrudan kullanılmalıdır.


2
Kişisel CheckObjectImpl kutuları bir içine null object, oysa CheckNullableImplboks kullanmaz. Bu yüzden karşılaştırma çok unfare. Bu sadece belirtildiği gibi konu da yararsız nedeni, ücret değil kabul cevap , derleyici yeniden yazar !=için HasValueen azından.
GSerg

2
Okuyucular yapısal yapısını görmezden gelmez Nullable<T>, siz yaparsınız (bunu bir kutuya koyarak object). != nullSolda bir boş değer ile uyguladığınızda , boş kutu desteği !=derleyici düzeyinde çalıştığı için hiçbir boks gerçekleşmez . Nullable'ı derleyiciden önce bir kutuya koyarak gizlediğinizde farklıdır object. Ne CheckObjectImpl(object o)de ne de kıyaslamanız prensipte mantıklı değildir.
GSerg

3
Benim sorunum bu web sitesinde içerik kalitesi umurumda olmasıdır. Gönderdiğiniz şey yanıltıcı veya yanlış. Eğer OP'ın soruya cevap teşebbüs olsaydı, o zaman cevap değiştirerek kanıtlamak kolaydır düz dışarı yanlış olduğu çağrıyı için CheckObjectImplonun ile vücut içi CheckObject. Ancak son yorumlarınız, bu 8 yıllık soruyu cevaplamaya karar verdiğinizde aslında tamamen farklı bir sorunuz olduğunu ortaya koyuyor ve bu da cevabınızı orijinal soru bağlamında yanıltıcı hale getiriyor. OP'nin sorduğu şey bu değildi.
GSerg

3
Kendinizi googles yapan bir sonraki adamın yerine koyun what is faster != or HasValue. Bu soruya ulaşıyor, cevabınıza göz atıyor, karşılaştırmanızı takdir ediyor ve "Gee, asla kullanmayacağım !=çünkü çok daha yavaş!" Diyor . Bu, daha sonra yayılmaya devam edeceği çok yanlış bir sonuçtur. Bu yüzden cevabınızın zararlı olduğuna inanıyorum - yanlış bir soruya cevap veriyor ve böylece şüphesiz okuyucuya yanlış bir sonuç veriyor. Para üstünü ne olur düşünün CheckNullableImpliçin de olmak return o != null;aynı kriter sonuç alırsınız.
GSerg

8
Cevabını tartışıyorum. Cevabınız aldatıcı bir şekilde, arasındaki farkı gösterir !=ve HasValueaslında object ove arasındaki farkı gösterir T? o. Eğer ben önerdi yaparsam olduğunu, yeniden CheckNullableImplsıra public static bool CheckNullableImpl<T>(T? o) where T: struct { return o != null; }bir kriter ile sona erecek, açıkça gösterir !=çok daha yavaş daha !=. Bu da, cevabınızın açıkladığı sorunun hiç !=vs HasValueile ilgili olmadığı sonucuna götürmelidir .
GSerg
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.