Ich habe es immer benutzt, Nullable<>.HasValue
weil mir die Semantik gefallen hat. Vor kurzem habe ich jedoch an der vorhandenen Codebasis einer anderen Person gearbeitet, die Nullable<> != null
stattdessen ausschließlich verwendet wurde.
Gibt es einen Grund, einen über den anderen zu verwenden, oder ist es eine reine Präferenz?
int? a; if (a.HasValue) // ...
vs.
int? b; if (b != null) // ...
HasValue
da ich denke, dass Wörter besser lesbar sind als Symbole. Es liegt jedoch ganz bei Ihnen und was zu Ihrem bestehenden Stil passt..HasValue
Dies ist sinnvoller, da esT?
angibt, dass der Typ vom Typ ist und nicht ein Typ, der nullbar sein kann, z. B. Zeichenfolgen.Antworten:
Der Compiler ersetzt Nullvergleiche durch einen Aufruf von
HasValue
, sodass es keinen wirklichen Unterschied gibt. Tun Sie einfach, was für Sie und Ihre Kollegen lesbarer / sinnvoller ist.quelle
int? x = null
gibt mir die Illusion, dass eine nullfähige Instanz ein Referenztyp ist. Die Wahrheit ist jedoch, dass Nullable <T> ein Werttyp ist. Es scheint, als würde ich eine NullReferenceException bekommen :int? x = null; Use(x.HasValue)
.Nullable<int>
anstelle vonint?
.Ich bevorzuge
(a != null)
es, dass die Syntax mit Referenztypen übereinstimmt.quelle
Nullable<>
es sich nicht um einen Referenztyp handelt.HasValue
weil es besser lesbar ist als!= null
Ich habe einige Nachforschungen angestellt, indem ich verschiedene Methoden verwendet habe, um einem nullbaren int Werte zuzuweisen. Folgendes ist passiert, als ich verschiedene Dinge getan habe. Sollte klären, was los ist. Denken Sie daran:
Nullable<something>
oder die Kurzschriftsomething?
ist eine Struktur, für die der Compiler anscheinend viel Arbeit leistet, damit wir sie mit null verwenden können, als wäre es eine Klasse.Wie Sie unten sehen werden,
SomeNullable == null
undSomeNullable.HasValue
wird immer wieder zurückkehren ein wahr oder falsch erwartet. Obwohl unten nicht gezeigt,SomeNullable == 3
ist es auch gültig (vorausgesetzt, SomeNullable ist einint?
).Während
SomeNullable.Value
uns einen Laufzeitfehler bekommt , wenn wir zugewiesennull
zuSomeNullable
. Dies ist in der Tat der einzige Fall, in dem Nullables dank einer Kombination überladener Operatoren, die überladen sind, ein Problem verursachen könnenobject.Equals(obj)
Methode und Compiler-Optimierung und Affengeschäft.Hier ist eine Beschreibung von Code, den ich ausgeführt habe, und welche Ausgabe er in Labels erzeugt hat:
Ok, versuchen wir die nächste Initialisierungsmethode:
Trotzdem wie zuvor.
int? val = new int?(null);
Beachten Sie, dass das Initialisieren mit , wobei null an den Konstruktor übergeben wurde, einen COMPILE-Zeitfehler erzeugt hätte, da der Wert des nullbaren Objekts NICHT nullwertfähig ist. Nur das Wrapper-Objekt selbst kann gleich null sein.Ebenso würden wir einen Kompilierungszeitfehler erhalten von:
ganz zu schweigen davon, dass dies
val.Value
sowieso eine schreibgeschützte Eigenschaft ist, was bedeutet, dass wir nicht einmal so etwas verwenden können wie:Aber auch hier können wir mit polymorphen überladenen impliziten Konvertierungsoperatoren Folgendes tun:
Sie müssen sich keine Sorgen um Polysomthing machen, was auch immer funktioniert, solange es richtig funktioniert? :) :)
quelle
Nullable<X>
VisualStudio 2013 und F12 eingeben, werden Sie feststellen, dass nur die Konvertierung von und nachX
und dieEquals(object other)
Methode überlastet werden. Ich denke jedoch, dass der Operator == diese Methode standardmäßig verwendet, sodass der Effekt der gleiche ist. Eigentlich wollte ich diese Antwort schon seit einiger Zeit aktualisieren, aber ich bin faul und / oder beschäftigt. Dieser Kommentar muss erstmal gemacht werden :)int? val = 42; val.GetType() == typeof(int)
. B. ). Nullable ist also nicht nur eine Struktur, die gleich null sein kann, sondern oft auch überhaupt nicht nullable! : D Auf die gleiche Weise boxen Sie, wenn Sie einen nullbaren Wert boxenint
, nichtint?
- und wenn derint?
keinen Wert hat, erhalten Sienull
anstelle eines nullbaren Boxwerts. Es bedeutet im Grunde, dass es selten Overhead gibt,Null
ein Typ in .NET? Können Sie auf den Teil in der CLR / C # -Spezifikation verweisen, in dem dies gesagt wird? Nullables sind in der CLR-Spezifikation gut definiert, ihr Verhalten ist keine "Implementierung einer Abstraktion" - es ist ein Vertrag . Aber wenn das Beste, was Sie tun können, Ad-Hominem-Angriffe sind, amüsieren Sie sich.In VB.Net. Verwenden Sie NICHT "IsNot Nothing", wenn Sie ".HasValue" verwenden können. Ich habe gerade einen mittleren Vertrauensfehler "Operation könnte die Laufzeit destabilisieren" gelöst, indem ich "IsNot Nothing" an einer Stelle durch ".HasValue" ersetzt habe. Ich verstehe nicht wirklich warum, aber im Compiler passiert etwas anders. Ich würde annehmen, dass "! = Null" in C # das gleiche Problem haben könnte.
quelle
HasValue
wegen der Lesbarkeit bevorzugen .IsNot Nothing
ist wirklich ein hässlicher Ausdruck (wegen der doppelten Verneinung).Wenn Sie linq verwenden und Ihren Code kurz halten möchten, empfehle ich, immer zu verwenden
!=null
Und deshalb:
Lassen Sie sich vorstellen , wir eine Klasse
Foo
mit einer Nullable - Doppel VariableSomeDouble
Wenn wir irgendwo in unserem Code alle Foo mit einem SomeDouble-Wert ungleich Null aus einer Sammlung von Foo erhalten möchten (vorausgesetzt, einige Foos in der Sammlung können auch null sein), haben wir am Ende mindestens drei Möglichkeiten, unsere Funktion zu schreiben (wenn wir benutze C # 6):
Und in solchen Situationen empfehle ich, immer die kürzere zu wählen
quelle
foo?.SomeDouble.HasValue
ist in diesem Kontext ein Fehler zur Kompilierungszeit (kein "Wurf" in meiner Terminologie), da sein Typbool?
nicht nur istbool
. (Die.Where
Methode will aFunc<Foo, bool>
.) Das ist(foo?.SomeDouble).HasValue
natürlich erlaubt , da das Typ hatbool
. Dies ist, was Ihre erste Zeile vom C # -Compiler (zumindest formal) intern "übersetzt" wird.Allgemeine Antwort und Faustregel: Wenn Sie die Möglichkeit haben (z. B. benutzerdefinierte Serializer zu schreiben), Nullable in einer anderen Pipeline zu verarbeiten als
object
- und ihre spezifischen Eigenschaften zu verwenden -, tun Sie dies und verwenden Sie Nullable-spezifische Eigenschaften. Aus konsequenter SichtHasValue
sollte daher bevorzugt werden. Konsequentes Denken kann Ihnen helfen, besseren Code zu schreiben, ohne zu viel Zeit mit Details zu verbringen. Zum Beispiel wird die zweite Methode um ein Vielfaches effektiver sein (hauptsächlich aufgrund von Inlinern und Boxen von Compilern, aber dennoch sind Zahlen sehr aussagekräftig):Benchmark-Test:
Benchmark-Code:
https://github.com/dotnet/BenchmarkDotNet wurde verwendet
PS . Die Leute sagen, dass Ratschläge "HasValue wegen konsequentem Denken bevorzugen" nicht verwandt und nutzlos sind. Können Sie die Leistung davon vorhersagen?
PPS- Leute fahren mit Minus fort, aber niemand versucht, die Leistung von vorherzusagen
CheckNullableGenericImpl
. Und dort hilft Ihnen der Compiler nicht beim Ersetzen!=null
durchHasValue
.HasValue
sollte direkt verwendet werden, wenn Sie an Leistung interessiert sind.quelle
CheckObjectImpl
Boxen sind nullbar in eineobject
, währendCheckNullableImpl
Boxen nicht verwendet wird. Daher ist der Vergleich sehr unfehlbar. Nicht nur ist es nicht Kost, sondern auch nutzlos , weil, wie in der erwähnten akzeptierten Antwort , die Compiler Schreibungen!=
aufHasValue
jeden Fall.Nullable<T>
( Sie boxen es in eineobject
). Wenn Sie!= null
mit einer Nullable auf der linken Seite anwenden , tritt kein Boxing auf, da die Unterstützung!=
für Nullables auf Compilerebene funktioniert. Es ist anders, wenn Sie die Nullable vor dem Compiler verbergen, indem Sie sie zuerst in eine Box einfügenobject
. WederCheckObjectImpl(object o)
Ihr Benchmark noch Ihr Benchmark sind grundsätzlich sinnvoll.CheckObjectImpl
durch seinen Körper im Inneren ersetzenCheckObject
. Ihre jüngsten Kommentare zeigen jedoch, dass Sie tatsächlich eine völlig andere Frage im Sinn hatten, als Sie sich entschieden haben, diese 8 Jahre alte Frage zu beantworten, was Ihre Antwort im Kontext der ursprünglichen Frage irreführend macht. Es war nicht das, wonach das OP fragte.what is faster != or HasValue
. Er kommt zu dieser Frage, blättert in Ihrer Antwort, schätzt Ihren Benchmark und sagt: "Gee, ich werde ihn niemals verwenden,!=
weil er eindeutig so viel langsamer ist!" Das ist eine sehr falsche Schlussfolgerung, die er dann weiter verbreiten wird. Deshalb glaube ich, dass Ihre Antwort schädlich ist - sie beantwortet eine falsche Frage und bringt dem ahnungslosen Leser eine falsche Schlussfolgerung. Überlegen Sie, was passiert , wenn Sie Ihre ändern ,CheckNullableImpl
um auch seinreturn o != null;
Sie erhalten die gleiche Benchmark - Ergebnis erhalten.!=
undHasValue
zeigt, obwohl sie tatsächlich den Unterschied zwischenobject o
und zeigtT? o
. Wenn Sie das tun, was ich vorgeschlagen habe, dh umschreibenCheckNullableImpl
alspublic static bool CheckNullableImpl<T>(T? o) where T: struct { return o != null; }
, erhalten Sie einen Benchmark, der deutlich zeigt, dass er!=
viel langsamer ist als!=
. Was Sie zu dem Schluss führen sollte, dass es sich bei dem in Ihrer Antwort beschriebenen Problem überhaupt nicht um!=
vs handeltHasValue
.