146

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.

Patrick
  • 674
  • 1
  • 8
  • 22
Nathan Koop
  • 24,803
  • 25
  • 90
  • 125

17 Answers17

164

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
  • 399,467
  • 113
  • 570
  • 794
  • 2
    You can omit the "T type" parameter since it's not used. – Michael Meadows Apr 21 '09 at 15:25
  • 1
    +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 Apr 21 '09 at 15:33
  • Luke- that's a little more than a nitpick- the code doesn't compile unless you do this! – RichardOD Jun 02 '09 at 13:14
  • See my answer below which handles nullable and standard types in the same methods. Also provides a boatload more options... :) – TheSoftwareJedi Jun 02 '09 at 13:25
  • 21
    This can be simplified a bit with string.IsNullOrWhiteSpace() if you use .Net4 – Sergej Andrejev Nov 12 '10 at 12:57
  • 1
    @andrefadila - To use: string sampleVendorId = ""; int? vendorId = sampleVendorId.ToNullable(); – minerva Sep 04 '15 at 21:56
  • 1
    The call conv.ConvertFrom doesn't convert a nullable type of T, which makes this function a little counter intuitive. You don't need a try catch in this function. These three lines of code makes it all: if (string.IsNullOrWhiteSpace(stringObject)) return null; var conv = TypeDescriptor.GetConverter(typeof(T)); return (T?)conv.ConvertFrom(stringObject); – David Jan 13 '17 at 18:13
  • Use result = (T)Convert.ChangeType(s, typeof(T), System.Globalization.CultureInfo.InvariantCulture); for conversion from object. TypeConverter.convertFrom does not support conversion between decimal and int. – Arci Oct 10 '17 at 14:31
  • @DaveWut No, if we the string is entered by user, it may be an invalid number like as "abc1357" instead of "1357". So we need to check if conversion fails... – S.Serpooshan Aug 01 '18 at 11:41
  • @JoelCoehoorn Good code! but I think we could do it also without using those try/catch block and GetConverter codes... as in [my answer](https://stackoverflow.com/a/51632318/2803565) – S.Serpooshan Aug 01 '18 at 11:45
57

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
  • 27,796
  • 4
  • 47
  • 63
  • 3
    IMHO this is the most elegant solution to the problem – Zaffiro Jun 02 '09 at 13:34
  • 4
    actually.. this solution doesn't work. changetype doesn't convert to nullable types. instead use typeconverter – AaronHS Nov 27 '11 at 11:16
  • That's what I need to know... I have to use the underlying type of a Nullable-Type when using the Convert.ChangeType-Method. Because it don't works with a Nullable-Typ for parameter conversionType. – Marcus.D Jul 17 '19 at 15:14
29

What about this:


double? amount = string.IsNullOrEmpty(strAmount) ? (double?)null : Convert.ToDouble(strAmount);

Of course, this doesn't take into account the convert failing.

bdukes
  • 152,002
  • 23
  • 148
  • 175
John Kraft
  • 6,811
  • 4
  • 37
  • 53
  • 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 Apr 21 '09 at 15:22
  • 1
    Sorry about that. Always forget the cast until the compiler screams. :) – John Kraft Apr 21 '09 at 15:39
  • this will fail if you aren't null and you try amount.HasValue and declare amount as var. – Steve Oct 29 '14 at 16:38
23

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
  • 34,421
  • 21
  • 109
  • 151
  • 2
    I don't think ignoring all conversion errors is the right thing to do. Also you should probably not swallow all kinds of exceptions. At least re-throw `OutOfMemoryException` if you cannot narrow it down to a fixed set of exception types. – Paul Groke Jan 24 '13 at 16:23
9

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
  • 36,616
  • 34
  • 155
  • 231
6

I like Joel's answer, but I've modified it slightly as I'm not a fan of eating exceptions.

    /// <summary>
    /// Converts a string to the specified nullable type.
    /// </summary>
    /// <typeparam name="T">The type to convert to</typeparam>
    /// <param name="s">The string to convert</param>
    /// <returns>The nullable output</returns>
    public static T? ToNullable<T>(this string s) where T : struct
    {
        if (string.IsNullOrWhiteSpace(s))
            return null;

        TypeConverter conv = TypeDescriptor.GetConverter(typeof (T));
        return (T) conv.ConvertFrom(s);
    }

    /// <summary>
    /// Attempts to convert a string to the specified nullable primative.
    /// </summary>
    /// <typeparam name="T">The primitive type to convert to</typeparam>
    /// <param name="data">The string to convert</param>
    /// <param name="output">The nullable output</param>
    /// <returns>
    /// True if conversion is successfull, false otherwise.  Null and whitespace will
    /// be converted to null and return true.
    /// </returns>
    public static bool TryParseNullable<T>(this string data, out T? output) where T : struct
    {
        try
        {
            output = data.ToNullable<T>();
            return true;
        }
        catch
        {
            output = null;
            return false;
        }
    }
Colin Place
  • 61
  • 1
  • 1
6

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
  • 182,639
  • 35
  • 285
  • 343
5

You can use the following with objects, unfortunately this does not work with strings though.

double? amount = (double?)someObject;

I use it for wrapping a session variable in a property (on a base page).. so my actual usage is (in my base page):

public int? OrganisationID
{
    get { return (int?)Session[Constants.Session_Key_OrganisationID]; }
    set { Session[Constants.Session_Key_OrganisationID] = value; }
}

I'm able to check for null in page logic:

if (base.OrganisationID == null)
    // do stuff
Scotty.NET
  • 12,533
  • 4
  • 42
  • 51
  • Hi thanks this solved it for me. FYI I was using VB.NET, and the VB equivilant `CType(Object, Nullable(Of Double))` works fine with strings – rayzinnz Apr 28 '16 at 01:08
  • is there a version of your first example that can be used with strings? – wazz Apr 08 '20 at 04:38
3

Here's something based on accepted answer. I removed the try/catch to make sure all the exceptions are not swallowed and not dealt with. Also made sure that the return variable (in accepted answer) is never initialized twice for nothing.

public static Nullable<T> ToNullable<T>(this string s) where T: struct
{
    if (!string.IsNullOrWhiteSpace(s))
    {
        TypeConverter conv = TypeDescriptor.GetConverter(typeof(T));

        return (T)conv.ConvertFrom(s);
    }

    return default(Nullable<T>);
}
PhilDulac
  • 1,305
  • 20
  • 32
3

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
  • 733,204
  • 149
  • 1,241
  • 1,454
3
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
  • 59,031
  • 16
  • 99
  • 143
3

There is a generic solution (for any type). Usability is good, but implementation should be improved: http://cleansharp.de/wordpress/2011/05/generischer-typeconverter/

This allows you to write very clean code like this:

string value = null;
int? x = value.ConvertOrDefault<int?>();

and also:

object obj = 1;  

string value = null;
int x = 5;
if (value.TryConvert(out x))
    Console.WriteLine("TryConvert example: " + x); 

bool boolean = "false".ConvertOrDefault<bool>();
bool? nullableBoolean = "".ConvertOrDefault<bool?>();
int integer = obj.ConvertOrDefault<int>();
int negativeInteger = "-12123".ConvertOrDefault<int>();
int? nullableInteger = value.ConvertOrDefault<int?>();
MyEnum enumValue = "SecondValue".ConvertOrDefault<MyEnum>();

MyObjectBase myObject = new MyObjectClassA();
MyObjectClassA myObjectClassA = myObject.ConvertOrDefault<MyObjectClassA>();
Pavel Hodek
  • 14,319
  • 3
  • 32
  • 37
  • Who was downvoting please add a comment what's wrong with this universal solution. – Pavel Hodek Oct 15 '12 at 08:09
  • 2
    Well, first there's something very wrong with your answer, and that's the "you can forget all other answers". Which would be wrong even if it were true (which it is not). And what's wrong with the "universal solution" is that it's full of bad performance (`typeName.IndexOf`? really?) and strang behaviour (the shown `TryConvert` function doesn't even handle null values correctly). – Paul Groke Jan 24 '13 at 16:31
2

My example for anonimous types:

private object ConvertNullable(object value, Type nullableType)
{
    Type resultType = typeof(Nullable<>).MakeGenericType(nullableType.GetGenericArguments());
    return Activator.CreateInstance(resultType, Convert.ChangeType(value, nullableType.GetGenericArguments()[0]));
}

...

Type anonimousType = typeof(Nullable<int>);
object nullableInt1 = ConvertNullable("5", anonimousType);
// or evident Type
Nullable<int> nullableInt2 = (Nullable<int>)ConvertNullable("5", typeof(Nullable<int>));
ADM-IT
  • 3,719
  • 1
  • 25
  • 26
2

Another variation. This one

  • Does not swallow exceptions
  • Throws a NotSupportedException if the type can not be converted from string. For instance, a custom struct without a type converter.
  • Otherwise returns a (T?)null if the string fails to parse. No need to check for null or whitespace.
using System.ComponentModel;

public static Nullable<T> ToNullable<T>(this string s) where T : struct
{
    var ret = new Nullable<T>();
    var conv = TypeDescriptor.GetConverter(typeof(T));

    if (!conv.CanConvertFrom(typeof(string)))
    {
        throw new NotSupportedException();
    }

    if (conv.IsValid(s))
    {
        ret = (T)conv.ConvertFrom(s);
    }

    return ret;
}
BurnsBA
  • 4,347
  • 27
  • 39
1

Let's add one more similar solution to the stack. This one also parses enums, and it looks nice. Very safe.

/// <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/blob/master/PangammaUtilities/Extensions/ToNullableStringExtension.cs

Pangamma
  • 731
  • 12
  • 28
0

The generic answer provided by "Joel Coehoorn" is good.

But, this is another way without using those GetConverter... or try/catch blocks... (i'm not sure but this may have better performance in some cases):

public static class StrToNumberExtensions
{
    public static short ToShort(this string s, short defaultValue = 0) => short.TryParse(s, out var v) ? v : defaultValue;
    public static int ToInt(this string s, int defaultValue = 0) => int.TryParse(s, out var v) ? v : defaultValue;
    public static long ToLong(this string s, long defaultValue = 0) => long.TryParse(s, out var v) ? v : defaultValue;
    public static decimal ToDecimal(this string s, decimal defaultValue = 0) => decimal.TryParse(s, out var v) ? v : defaultValue;
    public static float ToFloat(this string s, float defaultValue = 0) => float.TryParse(s, out var v) ? v : defaultValue;
    public static double ToDouble(this string s, double defaultValue = 0) => double.TryParse(s, out var v) ? v : defaultValue;

    public static short? ToshortNullable(this string s, short? defaultValue = null) => short.TryParse(s, out var v) ? v : defaultValue;
    public static int? ToIntNullable(this string s, int? defaultValue = null) => int.TryParse(s, out var v) ? v : defaultValue;
    public static long? ToLongNullable(this string s, long? defaultValue = null) => long.TryParse(s, out var v) ? v : defaultValue;
    public static decimal? ToDecimalNullable(this string s, decimal? defaultValue = null) => decimal.TryParse(s, out var v) ? v : defaultValue;
    public static float? ToFloatNullable(this string s, float? defaultValue = null) => float.TryParse(s, out var v) ? v : defaultValue;
    public static double? ToDoubleNullable(this string s, double? defaultValue = null) => double.TryParse(s, out var v) ? v : defaultValue;
}

Usage is as following:

var x1 = "123".ToInt(); //123
var x2 = "abc".ToInt(); //0
var x3 = "abc".ToIntNullable(); // (int?)null 
int x4 = ((string)null).ToInt(-1); // -1
int x5 = "abc".ToInt(-1); // -1

var y = "19.50".ToDecimal(); //19.50

var z1 = "invalid number string".ToDoubleNullable(); // (double?)null
var z2 = "invalid number string".ToDoubleNullable(0); // (double?)0
S.Serpooshan
  • 7,608
  • 4
  • 33
  • 61
  • @MassimilianoKraus may be, but it is a simple 12 lines code, written once, but using all the times. And, as I said, it should/may be faster than using those `TypeDescriptor.GetConverter`... codes. This is just another way. – S.Serpooshan Mar 17 '20 at 09:15
0
string text; 

private bool? convertToNullableBoolean()
{
   
    if (text == 'No')
    {
        return false;
    }
    if(text == 'Yes')
    {
        return true;
    }
    else
    {
        return null;
    }
}
Jeremy Caney
  • 7,102
  • 69
  • 48
  • 77
nmazari
  • 1
  • 1
  • 2
    Remember that Stack Overflow isn't just intended to solve the immediate problem, but also to help future readers find solutions to similar problems, which requires understanding the underlying code. This is especially important for members of our community who are beginners, and not familiar with the syntax. Given that, **can you [edit] your answer to include an explanation of what you're doing** and why you believe it is the best approach? – Jeremy Caney Apr 21 '23 at 00:19