当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.

 

 

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);

       }

 

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);

}

 

posted @ 2008-10-08 10:41  永不言败  阅读(1296)  评论(2编辑  收藏  举报