views:

1126

answers:

6

I'm using int as an example, but this applies to any value type in .Net

In .Net 1 the following would throw a compiler exception:

int i = SomeFunctionThatReturnsInt();

if( i == null ) //compiler exception here

Now (in .Net 2 or 3.5) that exception has gone.

I know why this is:

int? j = null; //nullable int

if( i == j )   //this shouldn't throw an exception

The problem is that because int? is nullable and int now has a implicit cast to int?. The syntax above is compiler magic. Really we're doing:

Nullable<int> j = null; //nullable int

//compiler is smart enough to do this
if( (Nullable<int>) i == j)   

//and not this
if( i == (int) j)

So now, when we do i == null we get:

if( (Nullable<int>) i == null )

Given that C# is doing compiler logic to calculate this anyway why can't it be smart enough to not do it when dealing with absolute values like null?

+1  A: 

Compiler still generates warning when you compare non-nullable type to null, which is just the way it should be. May be your warning level is too low or this was changed in recent versions (I only did that in .net 3.5).

ima
+2  A: 

Odd ... compiling this with VS2008, targetting .NET 3.5:

    static int F()
    {
        return 42;
    }

    static void Main(string[] args)
    {
        int i = F();

        if (i == null)
        {
        }
    }

I get a compiler warning

warning CS0472: The result of the expression is always 'false' since a value of type 'int' is never equal to 'null' of type 'int?'

And it generates the following IL ... which presumably the JIT will optimize away

L_0001: call int32 ConsoleApplication1.Program::F()
    L_0006: stloc.0 
    L_0007: ldc.i4.0 
    L_0008: ldc.i4.0 
    L_0009: ceq 
    L_000b: stloc.1 
    L_000c: br.s L_000e

Can you post a code snippet?

Rob Walker
The compiler correctly spots that it's never true, same as if you had done 1==2. It's smart enough to know that int can be implicitly cast to int? and that int? can be compared to null. I suspect the optimiser's smart enough to strip the whole block out.
Keith
+1  A: 

I don't think this is a compiler problem per se; an integer value is never null, but the idea of equating them isn't invalid; it's a valid function that always returns false. And the compiler knows; the code

bool oneIsNull = 1 == null;

compiles, but gives a compiler warning: The result of the expression is always 'false' since a value of type 'int' is never equal to 'null' of type '<null>'.

So if you want the compiler error back, go to the project properties and turn on 'treat warnings as errors' for this error, and you'll start seeing them as build-breaking problems again.

Steve Cooper
A: 

The warning is new (3.5 I think) - the error is the same as if I'd done 1 == 2, which it's smart enough to spot as never true.

I suspect that with full 3.5 optimisations the whole statement will just be stripped out, as it's pretty smart with never true evaluations.

While I might want 1==2 to compile (to switch off a function block while I test something else for instance) I don't want 1==null to.

Keith
A: 

It ought to be a compile-time error, because the types are incompatible (value types can never be null). It's pretty sad that it isn't.

DrPizza
Read the whole question, I explain why it occurs. The question is why it can't be treated as a special case.
Keith
When I say "ought" i mean that there is a moral imperative, not that that's what I expect the compiler to do. The rationale you describe is not the least bit compelling; they should make this a static compilation failure.
DrPizza
+1  A: 

The 2.0 framework introduced the nullable value type. Even though the literal constant "1" can never be null, its underlying type (int) can now be cast to a Nullable int type. My guess is that the compiler can no longer assume that int types are not nullable, even when it is a literal constant. I do get a warning when compiling 2.0:

Warning 1 The result of the expression is always 'false' since a value of type 'int' is never equal to 'null' of type 'int?'

Michael Meadows
Like I said - the compiler knows that int can be implicitly cast to int? and int? can be compared to null.The warning is a general one for any never-true comparison. 1==2 will throw the same warning.
Keith