null check auf non-nullables erlaubt?
-
wieso erlaubt mir C# non-nullables auf null zu prüfen?
bool x = true; if(x == null) // kann niemals wahr sein { x = false; }
wieder mal so eine "Designlücke" wie mit den teilweise-nullable Typen per default usw.
-
Das ist keine Designlücke und sollte normalerweise eine Warnung zur Folge haben.
Folgende Warnung erhalte ich dafür:
Das Ergebnis des Ausdrucks ist immer "false", da ein Wert vom Typ "bool" niemals NULL vom Typ "bool?" ist.
-
warum nur eine Warnung?
ein vergleich mit 10 oder "xyz" geht ja auch nicht - nicht der gleiche Wert-Typ - ist null implizit immer in alle Typen cast-bar?
-
Ich habe eine ganze Weile nach einem fiesen Fehler gesucht - jemand hatte ursprünglich mal
bool x = false; ... if(x == null) { //dieser code wurde nie ausgeführt }
in sowas verwandelt
bool? x = null; ... if(x == null) { //jetzt wird er ausgefuehrt }
alles ein wenig vergraben in viel Code - wenig Tests usw. - hätte C# schon im alten Code verhindern können - mit einem Error und keiner Warnung
-
Nein Null ist nicht implizit in alle Typen castbar. Das kannst du auch selbst ausprobieren indem du dem bool einfach mal "NULL" zuweist.
Lass mich jetzt nicht falsches sagen, aber ich denke, dass die Sprache / der Compiler den optimalsten Operator für den Vergleich heranzieht und den Nullable-Operator verwendet, weil der die Verwendung von 2 Nullable-Bools zum Vergleich erlaubt. Daher meckert der Compiler auch nicht mit einem Fehler, weil ein boolscher Wert in einen Nullable<bool> convertiert werden kann. - Null kann explizit auch in einen Nullable<bool> konvertiert werden.
Der Compiler ist ja auch nicht dazu da, dir das denken abzunehmen. - Darum sagt er dir auch ganz einfach dass der Ausdruck immer false ist.
if(true == null)
Ist ja gleichbedeutend mit:
bool? myVal = true; if(myVal == null)
Beide Ausdrücke werden nie "true" ergeben.
hätte C# schon im alten Code verhindern können - mit einem Error und keiner Warnung
Man kann Einstellungen in VS vornehmen, dass Warnungen als Fehler behandelt werden.
Auch behaupte ich, dass der eigentliche Ausdruck wie folgt übersetzt wird:
if ((bool?)true == (bool?)null)
-
weil ein boolscher Wert in einen Nullable<bool> convertiert werden kann
if(x == null)
wird dann einfach implizit zu
if(Nullable<bool>(x) == null)
was soll der tiefere Sinn davon sein?
ich glaube eher das alle Typen ein null-Compare erlauben und bei manchen ist das eben hart auf false eingestellt - könnte man vielleicht im IL Code sehen
Der Compiler ist ja auch nicht dazu da, dir das denken abzunehmen. - Darum sagt er dir auch ganz einfach dass der Ausdruck immer false ist.
er z.B. auch dazu zu da Code zu verweigern die niemals einen Sinn ergeben kann
genau so komisch wie
-Properties die keine "echten" ValueType sind
-das mal lokale Spezialisierungen einer Klasse mit einem generischem Typ der äußeren Klasse nicht mehr mit new instanzieren kann
und bestimmt noch mehr
-
MSIL für
bool myVal = false; if(myVal == null) ...
sieht wie folgt aus:
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // Code size 17 (0x11) .maxstack 2 .locals init (bool V_0, bool V_1) IL_0000: nop IL_0001: ldc.i4.0 IL_0002: stloc.0 IL_0003: ldc.i4.0 IL_0004: ldc.i4.0 IL_0005: ceq IL_0007: stloc.1 IL_0008: br.s IL_000a IL_000a: call int32 [mscorlib]System.Console::Read() IL_000f: pop IL_0010: ret } // end of method Program::Main
Der MSIL Code für den 2. Fall (explizit Nullable):
bool? myVal = false; if(myVal == null) ...
sieht wie folgt aus:
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // Code size 41 (0x29) .maxstack 2 .locals init (valuetype [mscorlib]System.Nullable`1<bool> V_0, bool V_1) IL_0000: nop IL_0001: ldloca.s V_0 IL_0003: ldc.i4.0 IL_0004: call instance void valuetype [mscorlib]System.Nullable`1<bool>::.ctor(!0) IL_0009: nop IL_000a: ldloca.s V_0 IL_000c: call instance bool valuetype [mscorlib]System.Nullable`1<bool>::get_HasValue() IL_0011: stloc.1 IL_0012: ldloc.1 IL_0013: brtrue.s IL_0022 IL_0015: nop IL_0016: ldstr "myVal is null!" IL_001b: call void [mscorlib]System.Console::WriteLine(string) IL_0020: nop IL_0021: nop IL_0022: call int32 [mscorlib]System.Console::Read() IL_0027: pop IL_0028: ret } // end of method Program::Main
Insofern wird es zumindest erst einmal unterschiedlich übersetzt. Wie man sieht, ist der Compiler sogar so intelligent, dass er den nie erfüllbaren Teil (bool = false) sogar ganz weglässt im IL-Code.
Ich behaupte aber weiterhin dass es an der gültigen Umwandlung von Nullable zu Non-Nullable und umgekehrt und dem damit gültigen Vergleichs-Operator liegt.
-
Ich behaupte aber weiterhin dass es an der gültigen Umwandlung von Nullable zu Non-Nullable und umgekehrt und dem damit gültigen Vergleichs-Operator liegt.
kannst du dieses implizite Verhalten mal explizit hin schreiben - oder geht das nur mit Magic unter der Haube
und wieso soll das gleich bedeutend sein?
if(true == null)
und das?
bool? myVal = true; if(myVal == null)
das sind doch zwei völlig unterschiedliche Dinge
-
Das es nicht exakt das gleiche ist, hat doch der IL-Code schon ausgesagt, insofern verstehe ich deine Aufregung jetzt nicht.
Was ich allerdings meinte ist, das folgende Zuweisung völlig legal ist:
bool myVal = false; bool? myNullableVal = myVal;
Aufgrund von Interpreter / Compiler-Magie wird deine Abfrage nun wohl zu "bool? == null". Da du aber explizit "false" zugewiesen hast bzw. das der InitialValue von bool ist, gibt dir der COmpiler eine Warnung aus und da er weiß, dass es nie "true" sein wird, wird es eben auch einfach Wegoptimiert.
BTW:
Folgendes ist auch gültiger Code:
bool myVal = false; bool? myNullableVal=null; if(myNullableVal == myVal) ...
-
Geht sogar bei Zahl-Literalen:
if (42 == null)
s.a. ideone
Unter C# okay with comparing value types to null wird dies auch behandelt.
Ich selber finde das aber auch nicht glücklich spezifiziert.
-
Ich selber finde das aber auch nicht glücklich spezifiziert.
das wurde geopfert damit das hantieren mit Nullables "schöner" aussieht