IComparable in the new Nullable<T> world

Our VP, Soma posted about the last minute changes to Nullable<T> in Visual Studio 2005. The changes have been available in August CTP for a while and comments have been coming in. Keep them coming.

Here is a thumbnail sketch of what the recent nullable changes do. I am really glad that we did those changes and as a program manager driving those changes at the last moment in the product cycle, I am glad that they are done and in your hands.

We wanted to unify reference nulls and value nulls (Nullable<T>) better. The main blocker was that in VS 2005 beta2, boxing a null value gave you a boxed Nullable<T>, not a null reference

  • Boxing operation unwraps the "Nullable wrapper" and gives you either a boxed T or a null ref. In turn, unboxing gives a T? or a T from a boxed T.
  • Interfaces implemented by T get "lifted" to T? (T? is the short hand syntax for Nullable<T> in C#). Likewise, "is" operator provides similar behavior - T? is IFoo if T is IFoo
  • Members don't get "lifted" from T to T? and T? does not satisfy the constraints that T does

Due to the last point, Peter Brown ran into an interesting issue. Earlier, Nullable<T> implemented IComparable so it could satisfy a generic constraint of the form. But with the latest changes, Nullable<T> no longer satisfies the IComparable constraint.

Here is the problem in a nutshell:

With the new Nullable<T> implementation, how can one write a generic method that can take two arguments of the same generic type – whether nullable or not and compare the arguments.

Let’s create a simple method signature for illustration

   // returns true if v1 < v2 and false otherwise
   static bool IsLess<T>(T v1, T v2)       
 

Here are some options:

1. Quick and dirty answer: Drop the IComparable constraints and cast to IComparable.

   return ((((IComparable)v1).CompareTo(v2) < 0) ? true 
      : false);

If you like, you can even check to make sure that if the argument is T?, then T implements IComparable

 

2. Write two separate methods – one for nullable and another for other value types. (More about the interesting problems with overload resolutions later)

 

But as one of my colleagues pointed out, there is an even better option – use the default comparer! It does the right thing – checks for IComparable / IComparable<T> implementation and otherwise provides a default comparer. So here is a brief sketch

    static bool IsLess3<T>(T v1, T v2)

    {

        Comparer<T> comparer = 
         System.Collections.Generic.Comparer<T>.Default;

        return (comparer.Compare(v1, v2) < 0 ? true 
         : false);

    }

As a bonus, the lookup is efficient because it is done only once and then cached. So subsequent calls don’t incur the lookup overhead.

 

Published 08 September 05 11:44 by Dinesh.Kulkarni

Comments

# Pete Brown's Blog said on September 9, 2005 9:23 AM:
If you recall from yesterday's blog entry, I was having some problems trying to find a way to compare...
# Pete Brown's Blog said on September 9, 2005 9:28 AM:
If you recall from yesterday's blog entry, I was having some problems trying to find a way to compare...
# kfarmer said on September 9, 2005 1:10 PM:
I may have missed the point, but was there a reason this could not be pushed into the definition of Nullable<T>?
# Dinesh's Cyberstation said on September 9, 2005 1:29 PM:
Before the recent Nullable&amp;lt;T&amp;gt; changes, some of you may have used&amp;nbsp;INullableValue to find a...
# Pete Brown's Blog said on September 9, 2005 1:38 PM:
If you recall from yesterday's blog entry, I was having some problems trying to find a way to compare...
# Dinesh's Cyberstation said on September 9, 2005 8:49 PM:
Before the recent Nullable&amp;lt;T&amp;gt; changes, some of you may have used&amp;nbsp;INullableValue to find a...
# DEVELOPMENT SITE - NOT MY PUBLIC BLOG said on June 28, 2008 11:37 PM:

If you recall from yesterday's blog entry , I was having some problems trying to find a way to compare some generics which may contain nullable types. My thanks go to Dinesh and his colleagues for solving this problem for me . Here's the new code; it's

New Comments to this post are disabled

About Dinesh.Kulkarni

I am a program manager in the Visual C# Product Unit of Microsoft. I am currently working on the LINQ project with specific responsibility for DLinq. Previously, I have been in a PM in SQL Server working on ObjectSpaces and DataSet. In pre-MS life, I have worked for companies ranging from startup to IBM on a wide range of software projects. Before I started working, I did M.S.E.E. and Ph.D. (CSE) from the University of Notre Dame and B.Tech. E.E. from IIT Bombay, India.

Search

This Blog

Syndication

Page view tracker