In the past, I have talked about how your feedback is a critical part of us building the right product.  Recently, we took a big DCR (Design Change Request) into Visual Studio 2005 that was in response to your feedback.  This was a hard call, because it was a big change that touched many components including the CLR.  Nonetheless, we decided to take this change at this late stage in the game because a) this was the right product design and I always believe in optimizing for the long-term and b) I had confidence in the team(s) to be able to get this work done in time for Visual Studio 2005.  This is a classic example of how we are listening to your feedback that results in a better product for all of us.

 

This particular work-item relates to the new System.Nullable data type included as part of the .Net Runtime in Visual Studio 2005.  If you want some context on what the Nullable type is all about, you can read this article in MSDN.

 

The August CTP for Visual Studio 2005 that is coming out in the next week or so will have this new implementation for the System.Nullable data type.

 

The C# team used the following example to illustrate why our original implementation could be confusing and how we changed support for Nullable type to make it more elegant, easy to understand and use for application developers.

 

We designed the Nullable type to be the platform solution, a single type that all applications can rely on to uniformly represent the null state for value types.  Languages like C# went ahead and built in further language features to make this new primitive feel even more at home.  The idea was to blur the subtle distinction between this new value-type null and the familiar reference-type null.  Yet, as it turns out, enough significant differences remained to cause quite a bit of confusion.

 

We soon realized the root of the problem sat in how we chose to define the Nullable type.  Generics were now available in the new runtime and it seemed quite simple to use this feature to build up a new parameterized type that could easily encode both a value type and an extra flag to describe its null state.  And by defining the Nullable type also as a value type we retained both the runtime behaviors and most of the performance of the underlying primitive. No need to special case anything in the runtime.  We could handle it all as just an addition to the runtime libraries, or so we thought.

 

As several of you pointed out, the Nullable type worked well only in strongly-typed scenarios.  Once an instance of the type was boxed (by casting to the base ‘Object’ type), it became a boxed value type, and no matter what its original ‘null’ state claimed, the boxed value-type was never null. 

 

      int? x = null;

      object y = x;

      if (y == null) {  // oops, it is not null?

        ...

      }

 

It also became increasingly difficult to tell whether a variable used in a generic type or method was ever null.

 

    void Foo<T>(T t) {

       if (t == null) {  // never true if T is a Nullable<S>?

       }

    }

 

Clearly this had to change.  We had a solution in Visual Studio 2005 Beta2 that gave users static methods that could determine the correct null-ness for nullable types in these more or less ‘untyped’ scenarios.  However, these methods were costly to call and difficult to remember to use.  The feedback you gave us was that you expected it to simply work right by default.

 

So we went back to the drawing board.  After looking at several different workarounds and options, it became clear to all that no amount of tweaking of the languages or framework code was ever going to get this type to work as expected.

 

The only viable solution was one that needed the runtime to change.  To do that, it would require concerted effort by a lot of different teams working under an already constrained schedule.  This was a big risk for us because so many components and products depend on the runtime that it has to be locked down much sooner than anything else.  Even a small change can have significant ripple effects throughout the company, adding work and causing delays.  Even the suggestion of a change caused quite a bit of turmoil.  Needless to say, many were against the proposal for very credible reasons.  It was a difficult decision to make. 

 

We were fortunate that so many here were willing to put in the extra work it took to explore the change, prototyping it and testing it, that a lot of the uncertainty and angst was put to rest, making the decision to go ahead all that much easier.

 

The outcome is that the Nullable type is now a new basic runtime intrinsic.  It is still declared as a generic value-type, yet the runtime treats it special.  One of the foremost changes is that boxing now honors the null state.  A Nullabe int now boxes to become not a boxed Nullable int but a boxed int (or a null reference as the null state may indicate.)  Likewise, it is now possible to unbox any kind of boxed value-type into its Nullable type equivalent. 

 

      int x = 10;

      object y = x; 

      int? z = (int?) y;  // unbox into a Nullable<int>

 

Together, these changes allow you to mix and match Nullable types with boxed types in a variety of loosely typed API’s such as reflection.  Each becomes an alternative, interchangeable representation of the other.

 

The C# language was then able to introduce additional behaviors that make the difference between the Nullable type and reference types even more seamless.  For example, since boxing now removes the Nullable wrapper, boxing instead the enclosed type, other kinds of coercions that also implied boxing became interesting.  It is now possible to coerce a Nullable type to an interface implemented by the enclosed type.

 

       int? x = 0;

       IComparable<int> ic = x;  // implicit coercion

 

I sincerely hope these changes were worth the effort and that application builders will find the definition of a common representation for null value types beneficial for the development of their products.  

 

Namaste!