Zawsze używałem, Nullable<>.HasValue
ponieważ lubiłem semantykę. Jednak ostatnio pracowałem nad czyjąś bazą kodów, w której używały one Nullable<> != null
wyłącznie zamiast tego.
Czy istnieje powód, aby używać jednego nad drugim, czy to czysta preferencja?
int? a; if (a.HasValue) // ...
vs.
int? b; if (b != null) // ...
HasValue
ponieważ uważam, że słowa są bardziej czytelne niż symbole. Wszystko zależy od Ciebie i od tego, co pasuje do Twojego stylu..HasValue
ma większy sens, ponieważ oznacza, że typ jest typu,T?
a nie typu, który może być zerowalny, np. ciągi znaków.Odpowiedzi:
Kompilator zastępuje porównania zerowe wywołaniem do
HasValue
, więc nie ma prawdziwej różnicy. Po prostu rób to, co jest bardziej czytelne / ma sens dla ciebie i twoich kolegów.źródło
int? x = null
daje mi złudzenie, że instancja zerowalna jest typem referencyjnym. Ale prawda jest taka, że Nullable <T> jest typem wartości. Czuje że mam NullReferenceException zrobić:int? x = null; Use(x.HasValue)
.Nullable<int>
zamiastint?
.Wolę,
(a != null)
aby składnia pasowała do typów referencji.źródło
Nullable<>
jest to typ odniesienia.HasValue
ponieważ jest bardziej czytelny niż!= null
Zrobiłem trochę badań na ten temat, używając różnych metod, aby przypisać wartości do wartości zerowej int. Oto, co się stało, gdy zrobiłem różne rzeczy. Powinien wyjaśnić, co się dzieje. Pamiętaj:
Nullable<something>
albo stenografiasomething?
jest strukturą, dla której kompilator wydaje się wykonywać wiele pracy, abyśmy mogli używać wartości zerowej, jakby to była klasa.Jak zobaczycie poniżej,
SomeNullable == null
iSomeNullable.HasValue
zawsze zwraca oczekiwane prawdziwe lub fałszywe. Chociaż nie pokazano poniżej,SomeNullable == 3
jest również poprawny (zakładając, że SomeNullable jest anint?
).Podczas gdy
SomeNullable.Value
dostaje nam błąd czasu wykonania, jeśli przypisaliśmynull
doSomeNullable
. Jest to w rzeczywistości jedyny przypadek, w którym wartości zerowe mogą sprawić nam problem, dzięki kombinacji przeciążonych operatorów, przeciążonychobject.Equals(obj)
metoda oraz optymalizacja kompilatora i małpia firma.Oto opis jakiegoś kodu, który uruchomiłem, i jaki wynik wygenerował w etykietach:
Ok, spróbujmy następnej metody inicjalizacji:
Wszystko tak samo jak poprzednio. Należy pamiętać, że inicjowanie za pomocą
int? val = new int?(null);
, z pustym przekazanym do konstruktora, spowodowałoby błąd czasowy KOMPILACJA, ponieważ WARTOŚĆ obiektu zerowalnego NIE jest dopuszczalna. Tylko sam obiekt otoki może mieć wartość null.Podobnie otrzymalibyśmy błąd czasu kompilacji z:
nie wspominając, że i tak
val.Value
jest to właściwość tylko do odczytu, co oznacza, że nie możemy nawet użyć czegoś takiego:ale znowu polimorficzne przeciążone niejawne operatory konwersji pozwalają nam na:
Nie musisz się jednak martwić o wieloskładnikowe, o ile działa dobrze? :)
źródło
Nullable<X>
w VisualStudio 2013 i F12, zobaczysz, że przeciąża tylko konwersję do i zX
orazEquals(object other)
metodę. Myślę jednak, że operator == domyślnie używa tej metody, więc efekt jest taki sam. Już od jakiegoś czasu chciałem zaktualizować tę odpowiedź, ale jestem leniwy i / lub zajęty. Ten komentarz na razie będzie musiał zrobić :)int? val = 42; val.GetType() == typeof(int)
.). Więc nie tylko jest zerowalne strukturą, która może być równa zeru, ale często nie jest wcale zerowalne! : D W ten sam sposób, gdy umieszczasz w polu wartość zerowalną, boksujeszint
, a nieint?
- a gdyint?
nie ma ona wartości, otrzymujesznull
zamiast wartości zerowanej w pudełku. Zasadniczo oznacza to, że właściwie nie ma narzutów związanych z prawidłowym użyciemNull
typ .NET? Czy możesz wskazać część specyfikacji CLR / C #, w której jest to powiedziane? Wartości null są dobrze zdefiniowane w specyfikacji CLR, ich zachowanie nie jest „implementacją abstrakcji” - to umowa . Ale jeśli najlepsze, co możesz zrobić, to ataki ad hominem, baw się dobrze.W VB.Net. NIE używaj „IsNot Nothing”, gdy możesz użyć „.HasValue”. Właśnie rozwiązałem problem „Operacja może zdestabilizować środowisko uruchomieniowe” Średni błąd zaufania, zastępując „IsNot Nothing” słowem „.HasValue” w jednym miejscu. Naprawdę nie rozumiem dlaczego, ale w kompilatorze dzieje się inaczej. Zakładam, że „! = Null” w języku C # może mieć ten sam problem.
źródło
HasValue
ze względu na czytelność.IsNot Nothing
jest naprawdę brzydkim wyrażeniem (z powodu podwójnej negacji).Jeśli używasz linq i chcesz skrócić swój kod, polecam zawsze używać
!=null
I dlatego:
Wyobraźmy sobie, że mamy klasę
Foo
z podwójną zmienną nullSomeDouble
Jeśli gdzieś w naszym kodzie chcemy uzyskać wszystkie Foo z wartością zerową SomeDouble z kolekcji Foo (zakładając, że niektóre foos w kolekcji również mogą mieć wartość null), otrzymamy co najmniej trzy sposoby na napisanie naszej funkcji (jeśli użyj C # 6):
I w takiej sytuacji polecam zawsze wybierać krótszą
źródło
foo?.SomeDouble.HasValue
w tym kontekście występuje błąd czasu kompilacji (nie „rzut” w mojej terminologii), ponieważ jego typ tobool?
nie tylkobool
. (.Where
Metoda chce aFunc<Foo, bool>
.) Oczywiście można to zrobić(foo?.SomeDouble).HasValue
, ponieważ ma on typbool
. Oto, jak twój pierwszy wiersz jest „tłumaczony” wewnętrznie przez kompilator C # (przynajmniej formalnie).Ogólna odpowiedź i ogólna zasada: jeśli masz opcję (np. Pisanie niestandardowych serializatorów), aby przetworzyć wartość Nullable w innym potoku niż
object
- i użyć ich określonych właściwości - zrób to i użyj właściwości Nullable. Dlatego z punktu widzenia konsekwentnego myśleniaHasValue
należy preferować. Konsekwentne myślenie może pomóc Ci napisać lepszy kod, nie poświęcając zbyt wiele czasu na szczegóły. Np. Druga metoda będzie wielokrotnie skuteczniejsza (głównie ze względu na wstawianie i boksowanie kompilatorów, ale wciąż liczby są bardzo wyraziste):Test porównawczy:
Kod testu porównawczego:
Wykorzystano https://github.com/dotnet/BenchmarkDotNet
PS . Ludzie mówią, że rada „wolę HasValue ze względu na konsekwentne myślenie” nie jest powiązana i bezużyteczna. Czy potrafisz przewidzieć wydajność tego?
Ludzie PPS kontynuują minus, ale nikt nie próbuje przewidzieć wydajności
CheckNullableGenericImpl
. I tam kompilator nie pomoże wymianie!=null
zHasValue
.HasValue
należy stosować bezpośrednio, jeśli jesteś zainteresowany wydajnością.źródło
CheckObjectImpl
pola nullable naobject
, podczasCheckNullableImpl
gdy nie używa boks. Zatem porównanie jest bardzo niejednoznaczne. Nie tylko nie jest taryfą, jest także bezużyteczne, ponieważ, jak zauważono w przyjętej odpowiedzi , kompilator!=
iHasValue
tak przepisuje .Nullable<T>
, co robisz (umieszczając go w poluobject
). Po zastosowaniu!= null
ze znakiem zerowym po lewej stronie nie występuje boksowanie, ponieważ obsługa wartości!=
zerowych działa na poziomie kompilatora. Jest inaczej, gdy ukrywasz zerowalną wartość przed kompilatorem, najpierw umieszczając ją w poluobject
. AniCheckObjectImpl(object o)
twój test porównawczy nie ma w zasadzie sensu.CheckObjectImpl
jego ciałem w środkuCheckObject
. Jednak twoje ostatnie komentarze ujawniają, że w rzeczywistości miałeś na myśli zupełnie inne pytanie, kiedy zdecydowałeś się odpowiedzieć na to 8-letnie pytanie, co sprawia, że twoja odpowiedź jest myląca w kontekście pierwotnego pytania. Nie o to pytał PO.what is faster != or HasValue
. Dochodzi do tego pytania, przegląda twoją odpowiedź, docenia twój punkt odniesienia i mówi: „Rany, nigdy nie użyję,!=
ponieważ jest o wiele wolniejszy!” To bardzo błędny wniosek, który następnie będzie się rozprzestrzeniał. Dlatego uważam, że twoja odpowiedź jest szkodliwa - odpowiada na złe pytanie, a tym samym wysuwa błędny wniosek u niczego niepodejrzewającego czytelnika. Zastanów się, co się dzieje, kiedy zmienićCheckNullableImpl
, aby również byćreturn o != null;
Dostaniesz ten sam wynik benchmarku.!=
aHasValue
kiedy w rzeczywistości pokazuje różnicę międzyobject o
iT? o
. Jeśli zrobisz to, co zasugerowałem, to znaczy przepiszCheckNullableImpl
jakopublic static bool CheckNullableImpl<T>(T? o) where T: struct { return o != null; }
, skończysz testem porównawczym, który wyraźnie pokazuje, że!=
jest znacznie wolniejszy niż!=
. Co powinno doprowadzić cię do wniosku, że problem, który opisuje twoja odpowiedź, w ogóle nie dotyczy!=
kontraHasValue
.