19

Perhaps my Google-Fu has failed me, but I haven't been able to determine if comparing a nullable in .NET will always be less than something else.

I've got some code similar to this

MyClass findLatest(List<MyClass> items){
    DateTime? latest_tstamp = null;
    MyClass latest_item = null;
    foreach(var item in items){
        if (latest_tstamp < item.tstamp){
            latest_tstamp = item.tstamp;
            latest_item = item;
        }
    }
    return latest_item;
}

It's seemed to work in the few limited cases I've tried (item.tstamp is declared DateTime? tstamp as well, of course).

Is this guaranteed behavior?

Conclusion(?)

Based on the answers (and Jon Skeet's [answer on another question]), I've gone with the following check:

if (item.tstamp != null &&
    (latest_tstamp == null || latest_tstamp < item.tstamp)){
    // do stuff
}
Community
  • 1
  • 1
Wayne Werner
  • 49,299
  • 29
  • 200
  • 290
  • 1
    Related, but not a duplicate: http://stackoverflow.com/questions/9294961/impose-a-sorting-rule-for-nullable-values-using-linq – user7116 Jul 11 '12 at 17:02
  • 1
    @sixlettervariables, Jon Skeet's answer is actually more helpful for my situation than the question I asked. Hooray for crowdsourcing! – Wayne Werner Jul 11 '12 at 18:16

6 Answers6

23

This is behavior is guaranteed by the C# specification. The result of < on nullable value-types is false if any of them is null. Reference types on the other hand might exhibit different behavior.

Still I wouldn't recommend using this. It's hard to understand this code. I'd prefer an explicit null check, or just a boolean flag isFirstElement instead of using a nullable in the first place.

7.2.7 Lifted operators

Lifted operators permit predefined and user-defined operators that operate on non-nullable value types to also be used with nullable forms of those types. Lifted operators are constructed from predefined and user-defined operators that meet certain requirements, as described in the following:

...

  • For the relational operators
         <   >   <=   >=
    a lifted form of an operator exists if the operand types are both non-nullable value types and if the result type is bool. The lifted form is constructed by adding a single ? modifier to each operand type. The lifted operator produces the value false if one or both operands are null. Otherwise, the lifted operator unwraps the operands and applies the underlying operator to produce the bool result.

(Quoted from C# Language Specification Version 3.0)

Community
  • 1
  • 1
CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
  • I guess I didn't really explain that my goal was to *find* the first element. I've got a list of "things" that have a timestamp, and because it's coming from a database `null` is a perfectly valid value. So I'm trying to find the item with the latest timestamp. – Wayne Werner Jul 11 '12 at 18:06
  • In that case I'd use an explicit `null` check. – CodesInChaos Jul 11 '12 at 18:18
6

Quote from MSDN:

When you perform comparisons with nullable types, if the value of one of the nullable types is null and the other is not, all comparisons evaluate to false except for != (not equal). It is important not to assume that because a particular comparison returns false, the opposite case returns true. In the following example, 10 is not greater than, less than, nor equal to null. Only num1 != num2 evaluates to true.

An equality comparison of two nullable types that are both null evaluates to true.

Lucero
  • 59,176
  • 9
  • 122
  • 152
2

In this case it will never be true. A comparison between nullable values where one of the values is null always produces false. Hence the if comparison will never be true here and latest_item will never be set to a value

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
2

Just an addition, you can call Nullable.GetValueOrDefault()

Example:

if (dateTime1.GetValueOrDefault() < dateTime2)
  ...
m02ph3u5
  • 3,022
  • 7
  • 38
  • 51
0

Regardless of compiler rules, the code isn't very readable. You should definitely have null checking explicitly (IMO):

if (latest_tstamp == null)
Erik Philips
  • 53,428
  • 11
  • 128
  • 150
0

This is not guaranteed to work for all types, because the < operator can be overridden to make sense for a specific class, and that code might not take null into account. For the standard operators for Nullable<T>, null will compare to be less than a value.

But if you are dealing with custom types, consider that their operators might not take nulls into account. For instance, look at this silly example:

void Main()
{
    Foo x = null;
    Foo y = new Foo();

    Console.WriteLine(x < y);
    Console.WriteLine(x > y);
}

class Foo 
{
    public static bool operator <(Foo lhs, Foo rhs)
    {
        if (ReferenceEquals(lhs, null))
            return false;
        return true;
    }

    public static bool operator >(Foo lhs, Foo rhs)
    {
        return false;
    }
}

I agree with the other answer here, that even though your code might work, it would be nicer to have explicit null checking.

driis
  • 161,458
  • 45
  • 265
  • 341