Considere el siguiente código de muestra:
class SampleClass
{
public long SomeProperty { get; set; }
}
public void SetValue(SampleClass instance, decimal value)
{
// value is of type decimal, but is in reality a natural number => cast
instance.SomeProperty = (long)value;
}
Ahora necesito hacer algo similar a través de la reflexión:
void SetValue(PropertyInfo info, object instance, object value)
{
// throws System.ArgumentException: Decimal can not be converted to Int64
info.SetValue(instance, value)
}
Tenga en cuenta que no puedo asumir que PropertyInfo siempre representa un valor largo, ni ese valor es siempre un decimal. Sin embargo, sé que el valor se puede convertir al tipo correcto para esa propiedad.
¿Cómo puedo convertir el parámetro 'valor' al tipo representado por la instancia PropertyInfo a través de la reflexión?
c#
reflection
casting
jeroenh
fuente
fuente
Convert.ChangeType(value, property.PropertyType);
aún puede fallar sivalue
no implementa laIConvertible
interfaz. Por ejemplo, siinfo.PropertyType
es algoIEnumerable
La respuesta de Thomas solo funciona para los tipos que implementan la interfaz IConvertible:
Este código compila una expresión linq que realiza el desempaquetado (si es necesario) y la conversión:
public static object Cast(this Type Type, object data) { var DataParam = Expression.Parameter(typeof(object), "data"); var Body = Expression.Block(Expression.Convert(Expression.Convert(DataParam, data.GetType()), Type)); var Run = Expression.Lambda(Body, DataParam).Compile(); var ret = Run.DynamicInvoke(data); return ret; }
La expresión lambda resultante es igual a (TOut) (TIn) Data donde TIn es el tipo de datos originales y TOut es el tipo dado
fuente
IEnumerable<object>
(donde esos objetos son cadenas) aIEnumerable<string>
. Desafortunadamente, recibo errores comoUnable to cast object of type 'System.Collections.Generic.IEnumerable'1[System.Object]' to type 'System.Collections.Generic.IEnumerable'1[System.String]'.
La respuesta de Thomas es correcta, pero pensé que agregaría mi hallazgo de que Convert.ChangeType no maneja la conversión a tipos que aceptan valores NULL. Para manejar tipos que aceptan valores NULL, utilicé el siguiente código:
void SetValue(PropertyInfo info, object instance, object value) { var targetType = info.PropertyType.IsNullableType() ? Nullable.GetUnderlyingType(info.PropertyType) : info.PropertyType; var convertedValue = Convert.ChangeType(value, targetType); info.SetValue(instance, convertedValue, null); }
Este código utiliza el siguiente método de extensión:
public static class TypeExtensions { public static bool IsNullableType(this Type type) { return type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)); }
fuente
Contribuyendo a la respuesta de jeroenh, agregaría que Convert.ChangeType se bloquea con un valor nulo, por lo que la línea para obtener el valor convertido debería ser:
var convertedValue = value == null ? null : Convert.ChangeType(value, targetType);
fuente
Cuando el tipo es una guía anulable, ninguna de las soluciones propuestas anteriormente funciona. Se lanza una excepción de conversión no válida de '
System.DBNull
' a 'System.Guid
'Convert.ChangeType
Para corregir ese cambio en:
var convertedValue = value == System.DBNull.Value ? null : Convert.ChangeType(value, targetType);
fuente
DBNull.Value
lugar de simplementenull
cuando obtiene valores nulos de la base de datos a través de ADO.Net. Verá lo mismo con nullable int, por ejemplo.Esta es una pregunta muy antigua, pero pensé en intervenir para ASP.NET Core Googlers.
En ASP.NET Core,
.IsNullableType()
está protegido (entre otros cambios), por lo que el código es un poco diferente. Aquí está la respuesta de @ jeroenh modificada para funcionar en ASP.NET Core:void SetValue(PropertyInfo info, object instance, object value) { Type proptype = info.PropertyType; if (proptype.IsGenericType && proptype.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) { proptype = new NullableConverter(info.PropertyType).UnderlyingType; } var convertedValue = Convert.ChangeType(value, proptype); info.SetValue(instance, convertedValue); }
fuente