从“System.Int32”转换为“System.Nullable`1 ]无效
Type t = typeof(int?); //will get this dynamically object val = 5; //will get this dynamically object nVal = Convert.ChangeType(val, t);//getting exception here
在上面的代码中,我得到了InvalidCastException。 对于上面,我可以简单地写int? nVal = val
int? nVal = val
,但上面的代码是dynamic执行的。
我得到一个包含在一个对象(这里为val)的值(非空types,如int,float等),我必须将其保存到另一个对象,通过将其转换为另一个types(可以或不可以为空) )。 什么时候
从“System.Int32”到“System.Nullable`1 [[System.Int32,mscorlib,Version = 4.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089]]的转换无效。
一个int
,应该是可转换/可types转换为nullable int
,这里有什么问题?
您必须使用Nullable.GetUnderlyingType
来获取Nullable.GetUnderlyingType
基础types。
这是我用来克服ChangeType
for Nullable
限制的方法
public static T ChangeType<T>(object value) { var t = typeof(T); if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) { if (value == null) { return default(T); } t = Nullable.GetUnderlyingType(t); } return (T)Convert.ChangeType(value, t); }
非generics方法:
public static object ChangeType(object value, Type conversion) { var t = conversion; if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) { if (value == null) { return null; } t = Nullable.GetUnderlyingType(t); } return Convert.ChangeType(value, t); }
对于上面,我可以简单地写int? nVal = val
其实你也做不到 没有从object
到Nullable<int>
隐式转换。 但是有一个从int
到Nullable<int>
的隐式转换,所以你可以这样写:
int? unVal = (int)val;
您可以使用Nullable.GetUnderlyingType
方法。
返回指定可空types的基础types参数 。
genericstypes定义是一个types声明,如Nullable,它包含一个types参数列表,而types参数列表声明一个或多个types参数。 closures的genericstypes是一个types声明,其中为types参数指定了特定的types。
Type t = typeof(int?); //will get this dynamically Type u = Nullable.GetUnderlyingType(t); object val = 5; //will get this dynamically object nVal = Convert.ChangeType(val, u);// nVal will be 5
这里是一个DEMO
。
我想我应该解释为什么该function不起作用:
1-引发exception的行如下所示:
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_FromTo", new object[] { value.GetType().FullName, targetType.FullName }));
实际上是在数组Convert.ConvertTypes之后的函数search,看看targer是一个Enum,当没有find它时,抛出上面的exception。
2- Convert.ConvertTypes被初始化为:
Convert.ConvertTypes = new RuntimeType[] { (RuntimeType)typeof(Empty), (RuntimeType)typeof(object), (RuntimeType)typeof(DBNull), (RuntimeType)typeof(bool), (RuntimeType)typeof(char), (RuntimeType)typeof(sbyte), (RuntimeType)typeof(byte), (RuntimeType)typeof(short), (RuntimeType)typeof(ushort), (RuntimeType)typeof(int), (RuntimeType)typeof(uint), (RuntimeType)typeof(long), (RuntimeType)typeof(ulong), (RuntimeType)typeof(float), (RuntimeType)typeof(double), (RuntimeType)typeof(decimal), (RuntimeType)typeof(DateTime), (RuntimeType)typeof(object), (RuntimeType)typeof(string) };
那么既然int?
不在ConvertTypes数组中,不是Enum引发的exception。
所以要恢复,为Convert.ChnageType函数工作,你有:
-
要转换的对象是IConvertible
-
目标types在ConvertTypes中,而不是
Empty
或者DBNull
(对于这两个抛出exception有一个明确的testing)
此行为是因为int
(和所有其他默认types)使用Convert.DefaultToType
作为IConvertibale.ToType implementation. and here is the code of the
implementation. and here is the code of the
使用ILSpy
extracted
implementation. and here is the code of the
DefaultToType implementation. and here is the code of the
internal static object DefaultToType(IConvertible value, Type targetType, IFormatProvider provider) { if (targetType == null) { throw new ArgumentNullException("targetType"); } RuntimeType left = targetType as RuntimeType; if (left != null) { if (value.GetType() == targetType) { return value; } if (left == Convert.ConvertTypes[3]) { return value.ToBoolean(provider); } if (left == Convert.ConvertTypes[4]) { return value.ToChar(provider); } if (left == Convert.ConvertTypes[5]) { return value.ToSByte(provider); } if (left == Convert.ConvertTypes[6]) { return value.ToByte(provider); } if (left == Convert.ConvertTypes[7]) { return value.ToInt16(provider); } if (left == Convert.ConvertTypes[8]) { return value.ToUInt16(provider); } if (left == Convert.ConvertTypes[9]) { return value.ToInt32(provider); } if (left == Convert.ConvertTypes[10]) { return value.ToUInt32(provider); } if (left == Convert.ConvertTypes[11]) { return value.ToInt64(provider); } if (left == Convert.ConvertTypes[12]) { return value.ToUInt64(provider); } if (left == Convert.ConvertTypes[13]) { return value.ToSingle(provider); } if (left == Convert.ConvertTypes[14]) { return value.ToDouble(provider); } if (left == Convert.ConvertTypes[15]) { return value.ToDecimal(provider); } if (left == Convert.ConvertTypes[16]) { return value.ToDateTime(provider); } if (left == Convert.ConvertTypes[18]) { return value.ToString(provider); } if (left == Convert.ConvertTypes[1]) { return value; } if (left == Convert.EnumType) { return (Enum)value; } if (left == Convert.ConvertTypes[2]) { throw new InvalidCastException(Environment.GetResourceString("InvalidCast_DBNull")); } if (left == Convert.ConvertTypes[0]) { throw new InvalidCastException(Environment.GetResourceString("InvalidCast_Empty")); } } throw new InvalidCastException(Environment.GetResourceString("InvalidCast_FromTo", new object[] { value.GetType().FullName, targetType.FullName })); }
另一方面,该转换是由Nullable类本身实现的,定义如下:
public static implicit operator T?(T value) { return new T?(value); } public static explicit operator T(T? value) { return value.Value; }