Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can TimeSpan and Guid Structs be compared to null?

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.

like image 225
dtroy Avatar asked Aug 04 '09 06:08

dtroy


2 Answers

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.

like image 131
Kenan E. K. Avatar answered Sep 23 '22 02:09

Kenan E. K.


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.

like image 34
Jon Skeet Avatar answered Sep 23 '22 02:09

Jon Skeet