![Creative The name of the picture]()
C# okay with comparing value types to null
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?");
}
}
}
if (1 == 2)
int
==
==
opeartor ==
==
9 Answers
9
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.
Nullable<T>
operator ==
operator !=
static bool operator == (SomeID a, String b)
Obsolete
null
SomeID?
null
It isn't an error, as there is a (int?
) conversion; it does generate a warning in the example given:
int?
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:
Main
MyValue(1)
.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);
}
The fact that a comparison can never be true doesn't mean that it's illegal. Nonetheless, no, a value type can ever be null
.
null
null
int?
Nullable<Int32>
int?
null
==
No, Int32 x
won't ever become null
.
Int32 x
null
If you are comparing an int to null
then the comparison operator that
takes two int?s is applicable.
"Why a comparison of a value type with null is a warning?" article will help you.
A value type cannot be null
, although it could be equal to null
(consider Nullable<>
). In your case the int
variable and null
are implicitly cast to Nullable<Int32>
and compared.
null
null
Nullable<>
int
null
Nullable<Int32>
I suspect that your particular test is just being optimized out by the compiler when it generates the IL since the test will never be false.
Side Note: It is possible to have a nullable Int32 use Int32? x instead.
I guess this is because "==" is a syntax sugar which actually represents call to System.Object.Equals
method that accepts System.Object
parameter. Null by ECMA specification is a special type which is of course derived from System.Object
.
System.Object.Equals
System.Object
System.Object
That's why there's only a warning.
[EDITED: made warnings into errors, and made operators explicit about nullable rather than the string hack.]
As per @supercat's clever suggestion in a comment above, the following operator overloads allow you to generate an error about comparisons of your custom value type to null.
By implementing operators that compare to nullable versions of your type, the use of null in a comparison matches the nullable version of the operator , which lets you generate the error via the Obsolete attribute.
Until Microsoft gives us back our compiler warning I'm going with this workaround, thanks @supercat!
public struct Foo
{
private readonly int x;
public Foo(int x)
{
this.x = x;
}
public override string ToString()
{
return string.Format("Foo {{x={0}}}", x);
}
public override int GetHashCode()
{
return x.GetHashCode();
}
public override bool Equals(Object obj)
{
return x.Equals(obj);
}
public static bool operator ==(Foo a, Foo b)
{
return a.x == b.x;
}
public static bool operator !=(Foo a, Foo b)
{
return a.x != b.x;
}
[Obsolete("The result of the expression is always 'false' since a value of type 'Foo' is never equal to 'null'", true)]
public static bool operator ==(Foo a, Foo? b)
{
return false;
}
[Obsolete("The result of the expression is always 'true' since a value of type 'Foo' is never equal to 'null'", true)]
public static bool operator !=(Foo a, Foo? b)
{
return true;
}
[Obsolete("The result of the expression is always 'false' since a value of type 'Foo' is never equal to 'null'", true)]
public static bool operator ==(Foo? a, Foo b)
{
return false;
}
[Obsolete("The result of the expression is always 'true' since a value of type 'Foo' is never equal to 'null'", true)]
public static bool operator !=(Foo? a, Foo b)
{
return true;
}
}
Foo a; Foo? b; ... if (a == b)...
if (a == null)
string
Object
ValueType
ReferenceThatCanOnlyBeNull
I think the best answer as to why the compiler accepts this is for generic classes. Consider the following class...
public class NullTester<T>
{
public bool IsNull(T value)
{
return (value == null);
}
}
If the compiler didn't accept comparisons against null
for value types, then it would essentially break this class, having an implicit constraint attached to its type parameter (i.e. it would only work with non-value-based types).
null
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
You can write
if (1 == 2)
as well. It's not the compiler's job to perform code path analysis; that's what static analysis tools and unit tests are for.– Aaronaught
Dec 29 '09 at 0:26