50

I currently use this handy conversion extension method to do conversions between types:

    public static T To<T>(this IConvertible obj)
    {
        return (T)Convert.ChangeType(obj, typeof(T));
    }

However, it doesn't like converting valid values to Nullable, for example, this fails:

    "1".To<int?>();

Obviously, 1 is easily converted to an (int?), but it gets the error:

    Invalid cast from 'System.String' to 'System.Nullable`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'.

This is an obviously simplified example, in reality I'm using it to do conversions from string types like so:

packageDb.Quantity = package.package.ElementDeep(Namespace + "PackageQuantity", Namespace + "ActualQuantity", Namespace + "Quantity").ValueOrNull().To<int?>();

If Convert.ChangeType doesn't like Nullable, anyone have any great ideas?

TheSoftwareJedi
  • 34,421
  • 21
  • 109
  • 151

8 Answers8

104
public static T To<T>(this IConvertible obj)
{
    Type t = typeof(T);
    Type u = Nullable.GetUnderlyingType(t);

    if (u != null)
    {
        return (obj == null) ? default(T) : (T)Convert.ChangeType(obj, u);
    }
    else
    {
        return (T)Convert.ChangeType(obj, t);
    }
}
LukeH
  • 263,068
  • 57
  • 365
  • 409
  • Thanks Luke! This is perfect. It was t.GetGenericTypeDefinition() == typeof(Nullable<>)) and Nullable.GetUnderlyingType(t) that really helped me out here. This is good stuff. Thanks again. – TheSoftwareJedi Apr 27 '09 at 15:47
  • 1
    Depending on your needs its helpful to change if (obj == null) TO if (obj == null || obj is String && obj.ToString() == "") – Jarrett Widman Nov 28 '10 at 05:25
  • As a slight simplification/clarification, if T is nullable, and obj is null, isn't `return default(T)` always the same as simply `return null` ? – goodeye Nov 25 '12 at 02:01
  • 1
    @goodeye: Yes, it is the same, but the compiler won't allow `return null` unless the method also has a `where T : class` constraint. – LukeH Nov 26 '12 at 00:34
  • @JarrettWidman I would implement the string use case you mention as a method overload where the parameter is a string instead of an IConvertible. Let the compiler do some optimization for you. – Richard Collette Nov 26 '12 at 16:49
  • This is gold. I ended up using a parameter of object instead for my purposes, but it works like a charm. Thanks a ton! – David L Jan 23 '13 at 21:50
  • Just bear in mind your `To` method behaves subtly different when it comes to handling of null input for value type outputs. For instance, `To(null)` explodes unlike `Convert.ToInt32(null)` which will return 0. This can be desirable, but is a slight difference in behaviour nevertheless, keep in mind. Also an input of `IConvertible` is not strictly needed since `Convert.ChangeType` will return the same object if the input and output types are the same, but its still a good constraint to have. – nawfal Jan 16 '14 at 09:41
5
public static T To<T>(this IConvertible obj) 
{
    Type t = typeof(T);
    if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
        t = t.GetGenericArguments()[0];

    return (T)Convert.ChangeType(obj, t); 
}

But if the conversion fail, it will throw an exception, not returning a null as should be expected.

Henrique
  • 415
  • 5
  • 9
3

I've ended up with this

private static T To<T>(this Object @object, Boolean returnDefaultOnException)
{
    Type type = typeof(T);
    Type underlyingTypeOfNullable = Nullable.GetUnderlyingType(type);
    try
    {
        return (T) Convert.ChangeType(@object, underlyingTypeOfNullable ?? type);
    }
    catch (Exception exception)
    {
        if (returnDefaultOnException)
            return default(T);
        String typeName = type.Name;
        if (underlyingTypeOfNullable != null)
            typeName += " of " + underlyingTypeOfNullable.Name;
        throw new InvalidCastException("Object can't be cast to " + typeName, exception);

    }
}
public static T To<T>(this Object @object) { return @object.To<T>(returnDefaultOnException: false); }
public static T ToOrDefault<T>(this Object @object) { return @object.To<T>(returnDefaultOnException: true); }

It behaves like the LINQ extension methods Single and SingleOrDefault and First and FirstOrDefault.

In short, To<T>() tries to convert and throws on failure while ToOrDefault<T>() tries to convert and returns default(T) on failure.

Nick Strupat
  • 4,928
  • 4
  • 44
  • 56
3

Maybe I'm missing the point, but in the instance of Nullable, how does your method provide either a readability, performance, or maintenance advantage over a simple cast, like (int?)1 ?

Aside from that, perhaps another extension method?

public static T? ToNullable<T>(this T obj) where T:struct
{
    return (T?)obj;
}

Edit

After reviewing your edit, why would the generic function that I provided not work as a substitute to your To<T> function in that line of code? You can't allow a conversion to Nullable for any type (which is why ChangeType doesn't work) because that generic only accepts value types. You'll either have to use a function like the one I provided or change your signature of To<T> to only accept value types and add a special case for Nullable<T>.

Adam Robinson
  • 182,639
  • 35
  • 285
  • 343
  • Is there a way to roll this into my existing method, and have it detect the nullable generic, and handle appropriately? – TheSoftwareJedi Apr 27 '09 at 14:47
  • Well, you could say this... if(typeof(T).IsGeneric && typeof(T).GetGenericTypeDefinition().Name.StartsWith("Nullable")) will tell you if the type is nullable. You can then apply the special case, but I'm not sure that can work when To accepts non-struct types. What would be the issue of just changing to use ToNullable when that's what you want? – Adam Robinson Apr 27 '09 at 14:55
2

Luke's solution was good for me (and obviously got his up vote) but I simplified it for me this way

    private static Type ResolveType(String typeName)
    {
        Type t = Type.GetType(typeName);
        if (t == null)
            return null;

        Type u = Nullable.GetUnderlyingType(t);

        if (u != null) {
            t = u;
        }
        return t;
    }

because I started from a string not from a type... thoughts?

Eugenio Miró
  • 2,398
  • 2
  • 28
  • 38
1

This is the method that I currently use (I got my answer on SO), it converts from string to nullable type:

    public static Nullable<T> ConvertToNullable<T>(this string s) where T : struct
    {
        if (!string.IsNullOrEmpty(s.Trim()))
        {
            TypeConverter conv = TypeDescriptor.GetConverter(typeof(Nullable<>).MakeGenericType(typeof(T)));
            return (Nullable<T>)conv.ConvertFrom(s);
        }
        return null;
    }
Community
  • 1
  • 1
Nathan Koop
  • 24,803
  • 25
  • 90
  • 125
0

extend @LukeH code:

public static T GetValue<T>(string Literal, T DefaultValue)
    {
        if (Literal == null || Literal == "" || Literal == string.Empty) return DefaultValue;
        IConvertible obj = Literal;
        Type t = typeof(T);
        Type u = Nullable.GetUnderlyingType(t);

        if (u != null)
        {
            return (obj == null) ? DefaultValue : (T)Convert.ChangeType(obj, u);
        }
        else
        {
            return (T)Convert.ChangeType(obj, t);
        }
    }
Hossein Shahabi
  • 327
  • 1
  • 2
  • 12
0

This method does what you need, and it looks nice while doing it.

    /// <summary>
    /// <para>More convenient than using T.TryParse(string, out T). 
    /// Works with primitive types, structs, and enums.
    /// Tries to parse the string to an instance of the type specified.
    /// If the input cannot be parsed, null will be returned.
    /// </para>
    /// <para>
    /// If the value of the caller is null, null will be returned.
    /// So if you have "string s = null;" and then you try "s.ToNullable...",
    /// null will be returned. No null exception will be thrown. 
    /// </para>
    /// <author>Contributed by Taylor Love (Pangamma)</author>
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="p_self"></param>
    /// <returns></returns>
    public static T? ToNullable<T>(this string p_self) where T : struct
    {
        if (!string.IsNullOrEmpty(p_self))
        {
            var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T));
            if (converter.IsValid(p_self)) return (T)converter.ConvertFromString(p_self);
            if (typeof(T).IsEnum) { T t; if (Enum.TryParse<T>(p_self, out t)) return t;}
        }

        return null;
    }

https://github.com/Pangamma/PangammaUtilities-CSharp/tree/master/src/StringExtensions

Pangamma
  • 731
  • 12
  • 28