当Convert.ChangeType遇到Nullable的时候(2)
当Convert.ChangeType遇到Nullable<T>的时候,转换的时候会出现InvalidCastException的异常,我在网上搜了一下,发现了这样的解决方法,如下:
Code
public static object ChangeType(object value, Type conversionType)
{
// Note: This if block was taken from Convert.ChangeType as is, and is needed here since we're
// checking properties on conversionType below.
if (conversionType == null)
{
throw new ArgumentNullException("conversionType");
} // end if
// If it's not a nullable type, just pass through the parameters to Convert.ChangeType
if (conversionType.IsGenericType &&
conversionType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
// It's a nullable type, so instead of calling Convert.ChangeType directly which would throw a
// InvalidCastException (per http://weblogs.asp.net/pjohnson/archive/2006/02/07/437631.aspx),
// determine what the underlying type is
// If it's null, it won't convert to the underlying type, but that's fine since nulls don't really
// have a type--so just return null
// Note: We only do this check if we're converting to a nullable type, since doing it outside
// would diverge from Convert.ChangeType's behavior, which throws an InvalidCastException if
// value is null and conversionType is a value type.
if (value == null)
{
return null;
} // end if
// It's a nullable type, and not null, so that means it can be converted to its underlying type,
// so overwrite the passed-in conversion type with this underlying type
NullableConverter nullableConverter = new NullableConverter(conversionType);
conversionType = nullableConverter.UnderlyingType;
} // end if
// Now that we've guaranteed conversionType is something Convert.ChangeType can handle (i.e. not a
// nullable type), pass the call on to Convert.ChangeType
return Convert.ChangeType(value, conversionType);
}
Code
Here's my ugly generic ChangeType method (from my ORMapper):
static public object ChangeType(object value, Type type) {
if (value == null && type.IsGenericType) return Activator.CreateInstance(type);
if (value == null) return null;
if (type == value.GetType()) return value;
if (type.IsEnum) {
if (value is string)
return Enum.Parse (type, value as string);
else
return Enum.ToObject(type, value);
}
if (!type.IsInterface && type.IsGenericType) {
Type innerType = type.GetGenericArguments()[0];
object innerValue = QueryHelper.ChangeType(value, innerType);
return Activator.CreateInstance(type, new object[] { innerValue });
}
if (value is string && type == typeof(Guid)) return new Guid(value as string);
if (value is string && type == typeof(Version)) return new Version(value as string);
if (!(value is IConvertible)) return value;
return Convert.ChangeType(value, type);
}
It may have more than you need, and there are bound to be even more special cases like Guid and Version, but it works so far.
I slightly modified your http://aspalliance.com/852 code. I canno tput code there due to server error.
I modified your code slightly, to make it generic. Thanks!
public static T ChangeType<T>(object value) {
Type conversionType = typeof(T);
if (conversionType.IsGenericType &&
conversionType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) {
if (value == null) { return default(T); }
conversionType = Nullable.GetUnderlyingType(conversionType); ;
}
return (T)Convert.ChangeType(value, conversionType);
}
Thanks was looking into this issue..
How about this as an option?
public static class Converter {
public static T ChangeType<T>(object value) {
TypeConverter tc = TypeDescriptor.GetConverter(typeof(T));
return (T)tc.ConvertFrom(value);
}
public static void RegisterTypeConverter<T, TC>() where TC : TypeConverter {
TypeDescriptor.AddAttributes(typeof(T), new TypeConverterAttribute(typeof(TC)));
}
}
This allows you to create new TypeDescriptors and add them at runtime, for any missing converters e.g. Version
public class VersionConverter : TypeConverter
{
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) {
string strvalue = value as string;
if (strvalue != null) {
return new Version(strvalue);
}
else {
return new Version();
}
}
}
Test code...
static void Main(string[] args) {
Converter.RegisterTypeConverter<Version, VersionConverter>();
int? i = Converter.ChangeType<int?>("123");
DateTime dt = Converter.ChangeType<DateTime>("20:33");
char c = Converter.ChangeType<char>("x");
Guid g = Converter.ChangeType<Guid>("{32F92EEB-A703-4eb7-A9F8-62E09F87D03F}");
Version v = Converter.ChangeType<Version>("1.2.3.4");
DateTime? k = Converter.ChangeType<DateTime?>(null);
}