Si encuentras algún fallo en tu código o proyecto, recuerda probar siempre en un entorno de testing antes aplicar el código al trabajo final.
Solución:
tienes que usar Nullable.GetUnderlyingType
para obtener el tipo subyacente de Nullable
.
Este es el método que utilizo para superar la limitación de ChangeType
por Nullable
public static T ChangeType(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);
método no genérico:
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);
Para arriba, simplemente podría escribir int? nVal = valor
En realidad, tampoco puedes hacer eso. No hay conversión implícita de object
para Nullable
. Pero hay es una conversión implícita de int
para Nullable
para que puedas escribir esto:
int? unVal = (int)val;
Puedes usar Nullable.GetUnderlyingType
método.
Devuelve el argumento de tipo subyacente del tipo que acepta valores NULL especificado.
Una definición de tipo genérico es una declaración de tipo, como Nullable, que contiene una lista de parámetros de tipo y la lista de parámetros de tipo declara uno o más parámetros de tipo. Un tipo genérico cerrado es una declaración de tipo donde se especifica un tipo particular para un parámetro de tipo.
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
Aquí está un DEMO
.
Creo que debería explicar por qué la función no funciona:
1- La línea que arroja la excepción es la siguiente:
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_FromTo", new object[]
value.GetType().FullName,
targetType.FullName
));
de hecho, la función de búsqueda en el array Convert.ConvertTypes después de eso, ve si el objetivo es un Enum y cuando no se encuentra nada, lanza la excepción anterior.
2- Convert.ConvertTypes se inicializa como:
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)
;
Así que desde el int?
no está en ConvertTypes array y no un Enum, se lanza la excepción.
Entonces, para resumir, para que la función Convert.ChnageType funcione, tiene:
-
El objeto a convertir es IConvertible
-
El tipo de destino está dentro de ConvertTypes y no
Empty
niDBNull
(Hay una prueba explícita en esos dos con excepción de lanzamiento)
Este comportamiento se debe a que int
(y todos los demás tipos predeterminados) utiliza Convert.DefaultToType
como IConvertibale.ToType implementation. and here is the code of the
Predeterminado a tipoextracted
utilizando ILSpy
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
));
por otro lado, la conversión se implementa mediante la propia clase Nullable y la definición es:
public static implicit operator T?(T value)
return new T?(value);
public static explicit operator T(T? value)
return value.Value;
Puntuaciones y comentarios
Si te mola el proyecto, tienes la opción de dejar una reseña acerca de qué te ha parecido esta división.