Condividi tramite


Creating Nullable When You Don't Know T

This bit came about from a post from Brad Wilson back in July that some of my Magenic buddies sent me. I won't repeat what his issues are (you can read it for yourself) but here's the solutions to his misunderstandings regarding nullable types.

Maybe I’m missing something here that’s subtle in his requirements but you should be able to do all of what he needs using the NullableConverter class. This type’s entire purpose is to provide “automatic conversion between a nullable type and its underlying primitive type”. Basically it knows about the “special cases” I mention later in this post…

So let's show you some code to show you how it works and more importantly, how the .Net runtime treats nullable types
Code:

 Int32? v = (Int32?)(new NullableConverter(typeof (Int32?))).ConvertFrom(1);
Debug.Assert(v.HasValue);//<- is a nullabletype
Debug.Assert(v == new int?(1)); <- always true as this is using the Equal(T) method under the covers
Debug.Assert(v is Int32?);
Debug.Assert(v.GetType() != typeof(Int32?)); //GetType() returns the T.GetType() value!
Debug.Assert(v.GetType() == typeof(Int32));
Debug.Assert(Nullable.GetUnderlyingType(typeof(Int32?)) == typeof(Int32));

Object o = (new NullableConverter(typeof (Int32?))).ConvertFrom(1);
Debug.Assert(o is Int32?); <- always true. Nullable<T> will always support implicit coercion to T!
Debug.Assert(o.GetType() != typeof(Int32?));
Debug.Assert(o.GetType() == typeof(Int32));

For those of you not paying attention, the catch going on here is that you CANNOT box a NullableType<T>! When converted the underlying value is boxed, not the nulalble wrapper. So if you set it to a variable of NullableType<T> explicitly however you retain the Nulalble wrapper on your valuetype.

Also beware however that the GetType() implementation on Nullable<T> actually will return the underlying type and not the nullable itself. Instead use the is keyword instead if you need to check (it’s also cheaper IL anyways than using the == operator). Note: this assumed that you are working with a variable of System.ValueType and NOT the underlying T parameterized type. Nullable<T> is always castable as T and vice versa so using the "is" operation is always true!

All this is due to the fact that in the last betas of the 2.0 runtime the team made a radical change to the memory management of the runtime with a new special case introduced to how System.Nullable<T> is handled. You’ve probably noticed that’s it’s the only value type that can directly be equated to null.

 v = null;//<- can’t normally do this with valuetypes!
Int32 I = null;//<- doesn’t compile!

 

So to make life all handy for everyone and be a mensch, here’s a snazzy little extension method you can drop on any valuetype to automatically do this conversion for you!

More Code:

 public static class Extensions
{
    public static T? CreateNullable<T>(this T item) where T : struct
    {
        T? v = (T?)(new NullableConverter(typeof(T?))).ConvertFrom(item);
        return v;
    }
}

Usage:

 
Int32 i = 1;
Int32? v = i.CreateNullable();

If you want to still dynamically bind yourself to the extension method then it's just some trivial reflection code and a generic delegate to get the job done:

 private delegate ValueType TestDelegate(T input) where T : struct;

//Assumes a MethodInfo class bound to the extension method or your choice of helper method
MethodInfo method = GetTargetMethod();
MethodInfo dispatchMethod = method.MakeGenericMethod(typeof (Int32));
Delegate d = Delegate.CreateDelegate(typeof(TestDelegate), dispatchMethod);
ValueType r = d.Invoke(1);

Enjoy!


Jimmy Zimms is thinkning that some .Net Expression-Fub is in order.