views:

348

answers:

5

I've noticed that some .NET structs can be compared to null. For example:

  TimeSpan y = new TimeSpan();
        if (y == null)
            return;

will compile just fine (the same with the Guid struct).
Now I know that stucts are value type and that the code above should not compile, unless there's an overload of operator == which takes an object. But, as far as I could tell there isn't.
I've looked at the class with Reflector, and also at the docs on MSDN.
The two of them do implement the following interfaces:

IComparable, IComparable<T>, IEquatable<T>

but, trying to implment the same Interfaces did not seem to help:

struct XX : IComparable, IComparable<XX>, IEquatable<XX> {
    public int CompareTo(Object obj) {
        return 0;
    }
    public int CompareTo (XX other){
        return 0;
    }
    public bool Equals (XX other){
        return false;
    }
    public override bool Equals(object value){
        return false;
    }
    public static int Compare(XX t1, XX t2){
        return 0;
    }
}

I'm using: .NET 2.0 Visual Studio 2005.

Does anyone has any idea what's the reason for this ? I am just trying to get a better understanding. This isn't an issue as I know I shouldn't compare structs to null anyway.

+6  A: 

This case is covered for generics in section 7.9.6 of the C# language specification.

The x == null construct is permitted even though T could represent a value type, and the result is simply defined to be false when T is a value type.

I dug through the spec for a bit and couldn't find a more general rule. Jon's answer indicates it's a nullable promotion issue.

This rule (or a similar variation) does seem to be being applied here. If you look at the reflected output closely you'll notice the comparison isn't there. The C# compiler is apparently optimizing this comparison away and replacing it with false.

For instance, if you type the following

var x = new TimeSpan();
var y = x == null;
Console.WriteLine(x);

Then decompile it you'll see the following

var x = new TimeSpan();
var y = false;
Console.WriteLine(x);
JaredPar
In fact, the compiler should issue a warning here, if it replaces the comparison. At least, it does in other circumstances. Mysterious.
Konrad Rudolph
It's also interesting that if you put it into a comparison ... if( x == null) you will get a warning for an int type, but not for a guid type. Maybe there is a secret way to null out a Guid. :)
JP Alioto
And why doesn't it work on custom structs?
Thomas Danecker
Custom structs covered by the connect issue I posted.
Marc Gravell
+3  A: 

I FOUND IT :)

The following gives a warning:

int i = 0;
if (i == null)
// ^^ Warning: The result of the expression is always 'false' since a value of
//             type 'int' is never equal to 'null' of type 'int?'

The compiler is just failing to emit the correct warning that the null you typed was converted to type TimeSpan? for the comparison.

Edit: The related section in the spec is §13.7.1 stating that null can be implicitly converted to any nullable type, and (the very difficult to read) section §13.7.2 stating a value type T can be implicitly converted to T?.

What I originally wrote:

Whatever's happening is something in the C# spec because like JaredPar says it compiles to simply false.

Note that this doesn't compile:

TimeSpan ts = new TimeSpan();
object o = null;
if (ts == o) // error, Operator '==' cannot be applied to operands of type 'System.TimeSpan' and 'object'
    ...
280Z28
+3  A: 

This problem was effectively introduced when nullable types were included. There's an implicit conversion from TimeSpan to TimeSpan?, and there's a comparison between TimeSpan? and the null value of that type.

The compiler issues a warning for some types which makes it clearer what it's trying to do:

int x = 10;
if (x == null)
{
    Console.WriteLine();
}

Gives this warning:

Test.cs(9,13): warning CS0472: The result of the expression is always 'false'
       since a value of type 'int' is never equal to 'null' of type 'int?'

I believe Marc Gravell and I worked out the circumstances under which the warning is given once... it's a shame it's not consistent.

Jon Skeet
Darn, you beat me to it ;-p For any custom struct (was it with Equals? ==?) it will **silently** (without warning) notice that it is redundant. IIRC it changed between C# 2.0 and C# 3.0; care to try C# 4.0? It may have been re-fixed...
Marc Gravell
@Marc, I tried the OP's repro on VS2010 Beta1 and it repros. Didn't try many other cases
JaredPar
@JaredPar - yes, when I found the connect issue I posted, Ed Maurer said he wasn't going to fix it. Sorry, my 2010 comment pre-dated me finding the article ;-p
Marc Gravell
Surely there's someone here we can blame this on :deal:
280Z28
@Marc- I've looked up your connect issue:http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=348850I thought it might be benefit others.Ed Maurer says there used to be a warning in previous versions, but as I mentioned I am not getting one in VS2005
dtroy
+3  A: 

It's the == operator.

The TimeSpan class has an overload of the equality operator:

public static bool operator ==(DateTime d1, DateTime d2)
{
     return (t1._ticks == t2._ticks);
}

This in itself doesn't make it possible to compare with null, but...

With the arrival of nullable types, each struct is implicitly convertible to its nullable type, so when you see something like

TimeSpan y = new TimeSpan();
if (y == null)
    return;

You don't see that this is happening:

TimeSpan y = new TimeSpan();
if ((Nullable<TimeSpan>)y == (Nullable<TimeSpan>)null)
    return;

Null gets the implicit conversion (implicit assignment?), but not all System.Object objects do:

TimeSpan y = new TimeSpan();
object o = null;
if (y == o) //compiler error
    return;

Okay, but the equality operator doesn't take nullable arguments, does it?

Well, msdn is of help here, stating:

The predefined unary and binary operators and any user-defined operators that exist for value types may also be used by nullable types. These operators produce a null value if [any of] the operands are null; otherwise, the operator uses the contained value to calculate the result.

So you effectively get a nullable implementation for each operator for free, with a fixed defined behaviour. The "contained value" mentioned above is the actual value the non-nullable operator would return.

kek444
You are right ! it's the operator== !Adding it to my custom struct will now allow me to compare to null!
dtroy
Yep. Maybe I should have added that sentence to the start of my post for clarity :)
kek444
+2  A: 

See also: C# 3 (.NET 3.5) version of csc fails to report CS0162 for unrechable code (struct/null)

Starting with the C# 3 compiler that means it sometimes doesn't even warn you about this ;-p

Because Guid / TimeSpan etc provide ==, they fall into this trap where it doesn't warn you.

Marc Gravell