2009-09-09 12 views
68

Consideremos el siguiente código de ejemplo:'colado' con la reflexión

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 tengo que 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 el PropertyInfo representa siempre un 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?

Respuesta

114
void SetValue(PropertyInfo info, object instance, object value) 
{ 
    info.SetValue(instance, Convert.ChangeType(value, info.PropertyType)); 
} 
35

La respuesta de Thomas es correcto, pero pensé que me gustaría añadir mi descubrimiento de que Convert.ChangeType no maneja la conversión a tipos anulables. Para manejar tipos anulables, he utilizado 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 hace uso del 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<>)); 
    } 
+0

esto funcionó para mí, explicación muy clara, gracias –

9

Contribuir a la respuesta de jeroenh, me gustaría añadir que los accidentes con un Convert.ChangeType valor nulo, por lo que la línea para conseguir el valor convertido debería ser:

var convertedValue = value == null ? null : Convert.ChangeType(value, targetType); 
+0

¡Estaba a punto de publicar esto! –

1

Cuando el tipo es un anulable Guid entonces ninguno de los trabajos soluciones propuestas anteriormente. fundido no válida del 'System.DBNull' a 'System.Guid' excepción se produce en Convert.ChangeType

Para solucionar ese cambio a:

var convertedValue = value == System.DBNull.Value ? null : Convert.ChangeType(value, targetType); 
+1

Este problema no es específico de Guid, sino que se debe al hecho de que se obtiene 'DBNull.Value' en lugar de simplemente' null' cuando se obtienen valores nulos de la base de datos a través de ADO.Net. Verá lo mismo con nullable int, por ejemplo. – jeroenh

28

Thomas respuesta sólo funciona para los tipos que implementan la interfaz IConvertible:

Para que la conversión tenga éxito, el valor debe implementar la interfaz IConvertible, porque el método simplemente ajusta una llamada a un método IConvertible apropiado. El método requiere que se apoye la conversión de valor a conversionType.

Este código compila una expresión LINQ que hace el unboxing (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) de datos en el estaño es el tipo de los datos originales y TOut es el tipo dado

+1

Esta es realmente la respuesta que vine buscando. Fundición dinámica no IConvertible. – jnm2

+0

Podrías marcarlo como aceptado :) – rafael

+1

Jeh, lo haría si fuera OP. – jnm2

Cuestiones relacionadas