Reputation: 25197
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.
Upvotes: 148
Views: 162372
Reputation: 1
string text;
private bool? convertToNullableBoolean()
{
if (text == 'No')
{
return false;
}
if(text == 'Yes')
{
return true;
}
else
{
return null;
}
}
Upvotes: 0
Reputation: 8388
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
Upvotes: 1
Reputation: 4929
Another variation. This one
NotSupportedException
if the type can not be converted from string
. For instance, a custom struct without a type converter.(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;
}
Upvotes: 2
Reputation: 807
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;
}
Upvotes: 1
Reputation: 1325
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>);
}
Upvotes: 3
Reputation: 4184
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>));
Upvotes: 2
Reputation: 14577
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>();
Upvotes: 3
Reputation: 12561
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
Upvotes: 5
Reputation: 61
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;
}
}
Upvotes: 6
Reputation: 35196
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;
}
}
Upvotes: 23
Reputation: 415600
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;
}
Upvotes: 165
Reputation: 59645
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;
}
}
}
Upvotes: 3
Reputation: 185593
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);
}
Upvotes: 6
Reputation: 28416
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>();
Upvotes: 57
Reputation: 6840
What about this:
double? amount = string.IsNullOrEmpty(strAmount) ? (double?)null : Convert.ToDouble(strAmount);
Of course, this doesn't take into account the convert failing.
Upvotes: 29
Reputation: 37378
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 {}
Upvotes: 10
Reputation: 754525
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.
Upvotes: 3