views:

8219

answers:

9

I am attempting to do some data conversion. Unfortunately, much of the data is in strings, where it should be int's or double, etc...

So what I've got is something like:

double? amount = Convert.ToDouble(strAmount);

The problem with this approach is if strAmount is empty, if it's empty I want it to amount to be null, so when I add it into the database the column will be null. So I ended up writing this:

double? amount = null;
if(strAmount.Trim().Length>0)
{
    amount = Convert.ToDouble(strAmount);
}

Now this works fine, but I now have five lines of code instead of one. This makes things a little more difficult to read, especially when I have a large amount of columns to convert.

I thought I'd use an extension to the string class and generic's to pass in the type, this is because it could be a double, or an int, or a long. So I tried this:

    public static class GenericExtension
    {
        public static Nullable<T> ConvertToNullable<T>(this string s, T type) where T: struct
        {
            if (s.Trim().Length > 0)
            {
                return (Nullable<T>)s;
            }
            return null;
        }
    }

But I get the error: Cannot convert type 'string' to 'T?'

Is there a way around this? I am not very familiar with creating methods using generics.

+1  A: 

There is no way around this. Nullable, as well as your method, is constrained to using only value types as it's argument. String is a reference type and hence is incompatible with this declaration.

JaredPar
+3  A: 

You might want to try:

TypeConverter conv = TypeDescriptor.GetConverter(typeof(int));
conv.ConvertFrom(mystring);

do your own null check and return int? if necessary. You'll also want to wrap that in a try {}

Andrew Bullock
+2  A: 
John Kraft
Nathan Koop
If you cast either of the return values to a double? (or int?, etc), then it will be able to convert them to the final double?. See the change above.
bdukes
Sorry about that. Always forget the cast until the compiler screams. :)
John Kraft
+25  A: 

Another thing to keep in mind is that the string itself might be null.

public static Nullable<T> ToNullable<T>(this string s) where T: struct
{
    Nullable<T> result = new Nullable<T>();
    try
    {
        if (!string.IsNullOrEmpty(s) && s.Trim().Length > 0)
        {
            TypeConverter conv = TypeDescriptor.GetConverter(typeof(T));
            result = (T)conv.ConvertFrom(s);
        }
    }
    catch { } 
    return result;
}
Joel Coehoorn
You can omit the "T type" parameter since it's not used.
Michael Meadows
Good catch, thanks.
Joel Coehoorn
+1, Just beat me to it. A small nitpick: the converted value needs to be assigned directly to result, not to result.Value. ie, "result = (T)conv.ConvertFrom(s);".
LukeH
Luke- that's a little more than a nitpick- the code doesn't compile unless you do this!
RichardOD
See my answer below which handles nullable and standard types in the same methods. Also provides a boatload more options... :)
TheSoftwareJedi
+8  A: 

You could try using the below extension method:

public static T? GetValueOrNull<T>(this string valueAsString)
    where T : struct 
{
    if (string.IsNullOrEmpty(valueAsString))
        return null;
    return (T) Convert.ChangeType(valueAsString, typeof(T));
}

This way you can do this:

double? amount = strAmount.GetValueOrNull<double>();
int? amount = strAmount.GetValueOrNull<int>();
decimal? amount = strAmount.GetValueOrNull<decimal>();
Michael Meadows
IMHO this is the most elegant solution to the problem
Zaffiro
+1  A: 
public static class GenericExtension
{
    public static T? ConvertToNullable<T>(this String s) where T : struct 
    {
        try
        {
            return (T?)TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(s);
        }
        catch (Exception)
        {
            return null;
        }
    }
}
Daniel Brückner
+3  A: 

Give this a shot...

public delegate bool TryParseDelegate<T>(string data, out T output);

public static T? ToNullablePrimitive<T>(this string data, 
    TryParseDelegate<T> func) where T:struct
{
    string.IsNullOrEmpty(data) return null;

    T output;

    if (func(data, out output))
    {
        return (T?)output;
    }

    return null;
}

Then call it like this...

void doStuff()
{
    string foo = "1.0";

    double? myDouble = foo.ToNullablePrimitive<double>(double.TryParse);

    foo = "1";

    int? myInt = foo.ToNullablePrimitive<int>(int.TryParse);

    foo = "haha";

    int? myInt2 = foo.ToNullablePrimitive<int>(int.TryParse);
}
Adam Robinson
+1  A: 

I wrote this generic type converter. It works with Nullable and standard values, converting between all convertible types - not just string. It handles all sorts of scenarios that you'd expect (default values, null values, other values, etc...)

I've been using this for about a year in dozens of production programs, so it should be pretty solid.

    public static T To<T>(this IConvertible obj)
    {
        Type t = typeof(T);

        if (t.IsGenericType
            && (t.GetGenericTypeDefinition() == typeof(Nullable<>)))
        {
            if (obj == null)
            {
                return (T)(object)null;
            }
            else
            {
                return (T)Convert.ChangeType(obj, Nullable.GetUnderlyingType(t));
            }
        }
        else
        {
            return (T)Convert.ChangeType(obj, t);
        }
    }

    public static T ToOrDefault<T>
                 (this IConvertible obj)
    {
        try
        {
            return To<T>(obj);
        }
        catch
        {
            return default(T);
        }
    }

    public static bool ToOrDefault<T>
                        (this IConvertible obj,
                         out T newObj)
    {
        try
        {
            newObj = To<T>(obj);
            return true;
        }
        catch
        {
            newObj = default(T);
            return false;
        }
    }

    public static T ToOrOther<T>
                           (this IConvertible obj,
                           T other)
    {
        try
        {
            return To<T>(obj);
        }
        catch
        {
            return other;
        }
    }

    public static bool ToOrOther<T>
                             (this IConvertible obj,
                             out T newObj,
                             T other)
    {
        try
        {
            newObj = To<T>(obj);
            return true;
        }
        catch
        {
            newObj = other;
            return false;
        }
    }

    public static T ToOrNull<T>
                          (this IConvertible obj)
                          where T : class
    {
        try
        {
            return To<T>(obj);
        }
        catch
        {
            return null;
        }
    }

    public static bool ToOrNull<T>
                      (this IConvertible obj,
                      out T newObj)
                      where T : class
    {
        try
        {
            newObj = To<T>(obj);
            return true;
        }
        catch
        {
            newObj = null;
            return false;
        }
    }
TheSoftwareJedi
A: 

You don't want to convert a String to a double?.

You want to write a function that takes a String and, following some reasonings which are specific to your project, returns a double?.

Then you will have everything in one line - the line that invokes your method, and it will be maintainable for future changes.

Daniel Daranas