Reputation: 54811
I have a string
and a Type
, and I want to return the string
value converted to that Type
.
public static object StringToType(string value, Type propertyType)
{
return Convert.ChangeType(value, propertyType, CultureInfo.InvariantCulture);
}
This returns an object
that I can use in a property set value call:
public static void SetBasicPropertyValueFromString(object target,
string propName,
string value)
{
PropertyInfo prop = target.GetType().GetProperty(propName);
object converted = StringToType(value, prop.PropertyType);
prop.SetValue(target, converted, null);
}
This works for most basic types, except nullables.
[TestMethod]
public void IntTest()
{ //working
Assert.AreEqual(1, ValueHelper.StringToType("1", typeof (int)));
Assert.AreEqual(123, ValueHelper.StringToType("123", typeof (int)));
}
[TestMethod]
public void NullableIntTest()
{ //not working
Assert.AreEqual(1, ValueHelper.StringToType("1", typeof (int?)));
Assert.AreEqual(123, ValueHelper.StringToType("123", typeof (int?)));
Assert.AreEqual(null, ValueHelper.StringToType(null, typeof (int?)));
}
NullableIntTest
fails on first line with:
System.InvalidCastException: Invalid cast from 'System.String' to 'System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'.
I'm having difficulty detemining if the type is nullable and changing the behaiour of the StringToType
method.
Behaviour I am after:
If string is null or empty, return null, else convert as per the non-nullable type.
Result
Like Kirill's answer, only with one ChangeType
call.
public static object StringToType(string value, Type propertyType)
{
var underlyingType = Nullable.GetUnderlyingType(propertyType);
if (underlyingType != null)
{
//an underlying nullable type, so the type is nullable
//apply logic for null or empty test
if (String.IsNullOrEmpty(value)) return null;
}
return Convert.ChangeType(value,
underlyingType ?? propertyType,
CultureInfo.InvariantCulture);
}
Upvotes: 8
Views: 4102
Reputation: 11964
You cannot use Convert.ChangeType on nullable types cause it is not inherited from IConvertible. You should rewrite your method.
public static object StringToType(string value, Type propertyType)
{
var underlyingType = Nullable.GetUnderlyingType(propertyType);
if(underlyingType == null)
return Convert.ChangeType(value, propertyType, CultureInfo.InvariantCulture);
return String.IsNullOrEmpty(value)
? null
: Convert.ChangeType(value, underlyingType, CultureInfo.InvariantCulture);
}
Upvotes: 6
Reputation: 4489
The code with help of Kirill Bestemyanov' snippet:
public static object StringToType<T>(string value)
{
return StringToType(value, typeof(T));
}
public static object StringToType(string value, Type propertyType)
{
var underlyingType = Nullable.GetUnderlyingType(propertyType);
if(underlyingType == null)
return Convert.ChangeType(value, propertyType, CultureInfo.InvariantCulture);
return String.IsNullOrEmpty(value)
? null
: Convert.ChangeType(value, underlyingType, CultureInfo.InvariantCulture);
}
And usage (WL
just writes to console). You were right, the generic method can't use int?
as generic parameter.
WL(StringToType("1", typeof (int?))); // -> 1
WL(StringToType<int>("1")); // -> 1
WL(StringToType<int?>("1")); // error, not compilable
WL(StringToType<Nullable<int>>("1")); // -> 1
Upvotes: 1
Reputation: 203812
The problem here is that the return value of ChangeType
, as well as your method, is object
. When you box any nullable type into an object
it doesn't box the nullable type. If the value, at runtime, is actually null it boxes a null
, and if it has a value it boxes the actual underlying value (rather than the nullable version).
int? i = 5;
object o = i;
Type t = o.GetType();//will be `int`, not `Nullable<int>`
This won't happen in the general case with any other type; Nullable<T>
has special compiler support to do this. You'll basically need to special case Nullable
in your code as well; If your method is passed a Nullable
type you'll need to first check the object for null
, and if not null
, use the underlying type of the Nullable
instead.
Upvotes: 2
Reputation: 739
You must use an appropriate cast method for each type you want to support.
int parsedInt;
int.TryParse("1", out parsedInt);
double parsedDouble;
double.TryParse("0.0d", out parsedDouble);
It is impossible for the compiler to figure out the type depending on the contents of the string. See the following links for more information about converting from string to scalars types: http://msdn.microsoft.com/en-us/library/bb397679.aspx and http://msdn.microsoft.com/en-us/library/bb384043.aspx.
Upvotes: 0
Reputation: 1970
public static object StringToType(string value, Type propertyType)
{
var underlyingType = Nullable.GetUnderlyingType(propertyType);
return underlyingType == null ? Convert.ChangeType(value, propertyType, CultureInfo.InvariantCulture) : Convert.ChangeType(value, underlyingType, CultureInfo.InvariantCulture);
}
Upvotes: 3
Reputation: 63562
try this:
prop.IsGenericType && Nullable.GetUnderlyingType(prop) == value.GetType()
Upvotes: 2