I ran into this today and have no idea why the C# compiler isn't throwing an error.
Int32 x = 1; if (x == null) { Console.WriteLine("What the?"); }
I'm confused as to how x could ever possibly be null. Especially since this assignment definitely throws a compiler error:
Int32 x = null;
Is it possible that x could become null, did Microsoft just decide to not put this check into the compiler, or was it missed completely?
Update: After messing with the code to write this article, suddenly the compiler came up with a warning that the expression would never be true. Now I'm really lost. I put the object into a class and now the warning has gone away but left with the question, can a value type end up being null.
public class Test { public DateTime ADate = DateTime.Now; public Test () { Test test = new Test(); if (test.ADate == null) { Console.WriteLine("What the?"); } } }
This is legal because operator overload resolution has a unique best operator to choose. There is an == operator that takes two nullable ints. The int local is convertible to a nullable int. The null literal is convertible to a nullable int. Therefore this is a legal usage of the == operator, and will always result in false.
Similarly, we also allow you to say "if (x == 12.6)", which will also always be false. The int local is convertible to a double, the literal is convertible to a double, and obviously they will never be equal.
It isn't an error, as there is a (int?
) conversion; it does generate a warning in the example given:
The result of the expression is always 'false' since a value of type 'int' is never equal to 'null' of type 'int?'
If you check the IL, you'll see that it completely removes the unreachable branch - it doesn't exist in a release build.
Note however that it doesn't generate this warning for custom structs with equality operators. It used to in 2.0, but not in the 3.0 compiler. The code is still removed (so it knows that the code is unreachable), but no warning is generated:
using System; struct MyValue { private readonly int value; public MyValue(int value) { this.value = value; } public static bool operator ==(MyValue x, MyValue y) { return x.value == y.value; } public static bool operator !=(MyValue x, MyValue y) { return x.value != y.value; } } class Program { static void Main() { int i = 1; MyValue v = new MyValue(1); if (i == null) { Console.WriteLine("a"); } // warning if (v == null) { Console.WriteLine("a"); } // no warning } }
With the IL (for Main
) - note everything except the MyValue(1)
(which could have side-effects) has been removed:
.method private hidebysig static void Main() cil managed { .entrypoint .maxstack 2 .locals init ( [0] int32 i, [1] valuetype MyValue v) L_0000: ldc.i4.1 L_0001: stloc.0 L_0002: ldloca.s v L_0004: ldc.i4.1 L_0005: call instance void MyValue::.ctor(int32) L_000a: ret }
this is basically:
private static void Main() { MyValue v = new MyValue(1); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With