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 🙂


Log in to reply