251 votos

Convert.ChangeType() falla en tipos que aceptan valores NULL

Quiero convertir una cadena en un valor de propiedad del objeto, cuyo nombre tengo como una cadena. Trato de hacerlo como:

string modelProperty = "Some Property Name";
string value = "SomeValue";
var property = entity.GetType().GetProperty(modelProperty);
if (property != null) {
    property.SetValue(entity, 
        Convert.ChangeType(value, property.PropertyType), null);
}

El problema es que esto está fallando y lanzando una excepción de reparto no válido cuando el tipo de propiedad es un tipo que acepta valores NULL. Este no es el caso de no poder convertir los valores - funcionarán si hago esto manualmente (por ejemplo DateTime? d = Convert.ToDateTime(value); ) que haya visto algunas preguntas similares pero aún no puede hacer que funcione.

347voto

LukeH Puntos 110965

No comprobado, pero tal vez que algo como esto funcionará:

string modelProperty = "Some Property Name";
string value = "SomeValue";

var property = entity.GetType().GetProperty(modelProperty);
if (property != null)
{
    Type t = Nullable.GetUnderlyingType(property.PropertyType)
                 ?? property.PropertyType;

    object safeValue = (value == null) ? null
                                       : Convert.ChangeType(value, t);

    property.SetValue(entity, safeValue, null);
}

62voto

BenAlabaster Puntos 20189

Tienes que conseguir el tipo subyacente con el fin de hacer que...

Intenta esto, la he usado con éxito con medicamentos genéricos:

//Coalesce to get actual property type...
Type t = property.PropertyType();
t = Nullable.GetUnderlyingType(t) ?? t;

//Coalesce to set the safe value using default(t) or the safe type.
safeValue = value == null ? default(t) : Convert.ChangeType(value, t);

Yo lo uso en un número de lugares en mi código, un ejemplo es un método auxiliar puedo usar para la conversión de los valores de base de datos en un typesafe manera:

public static T GetValue<T>(this IDataReader dr, string fieldName)
{
    object value = dr[fieldName];

    Type t = typeof(T);
    t = Nullable.GetUnderlyingType(t) ?? t;

    return (value == null || DBNull.Value.Equals(value)) ? 
        default(T) : (T)Convert.ChangeType(value, t);
}

Se llama:

string field1 = dr.GetValue<string>("field1");
int? field2 = dr.GetValue<int?>("field2");
DateTime field3 = dr.GetValue<DateTime>("field3");

Yo escribí una serie de entradas de blog, incluyendo en este http://www.endswithsaurus.com/2010_07_01_archive.html (Desplácese hacia abajo a la Adenda, @JohnMacintyre en realidad vio el error en mi código original que me llevó por el mismo camino que ahora está en). Tengo un par de pequeñas modificaciones, ya que el post que incluye la conversión de tipos enum también por lo que si su propiedad es una Enumeración aún puedes usar el mismo método de llamada. Acaba de agregar una línea en la comprobación de tipos enum y que vamos a las carreras con algo similar a:

if (t.IsEnum)
    return (T)Enum.Parse(t, value);

Normalmente tendrías algún error en la comprobación o el uso TryParse en lugar de Analizar, pero usted consigue el cuadro.

8voto

Eric Burcham Puntos 2621

Esto es un poco larga-ish para un ejemplo, pero este es un relativamente robusta, y que separa la tarea de conversión de valor desconocido de desconocido

Tengo un TryCast método que hace algo similar, y la toma de tipos que aceptan valores null en cuenta.

public static bool TryCast<T>(this object value, out T result)
{
    var type = typeof (T);

    // If the type is nullable and the result should be null, set a null value.
    if (type.IsNullable() && (value == null || value == DBNull.Value))
    {
        result = default(T);
        return true;
    }

    // Convert.ChangeType fails on Nullable<T> types.  We want to try to cast to the underlying type anyway.
    var underlyingType = Nullable.GetUnderlyingType(type) ?? type;

    try
    {
        // Just one edge case you might want to handle.
        if (underlyingType == typeof(Guid))
        {
            if (value is string)
            {
                value = new Guid(value as string);
            }
            if (value is byte[])
            {
                value = new Guid(value as byte[]);
            }

            result = (T)Convert.ChangeType(value, underlyingType);
            return true;
        }

        result = (T)Convert.ChangeType(value, underlyingType);
        return true;
    }
    catch (Exception ex)
    {
        result = default(T);
        return false;
    }
}

Por supuesto TryCast es un Método con un Parámetro de Tipo, por lo que para llamar dinámicamente usted tiene que construir la MethodInfo a ti mismo:

var constructedMethod = typeof (ObjectExtensions)
    .GetMethod("TryCast")
    .MakeGenericMethod(property.PropertyType);

A continuación, para establecer el real valor de la propiedad:

public static void SetCastedValue<T>(this PropertyInfo property, T instance, object value)
{
    if (property.DeclaringType != typeof(T))
    {
        throw new ArgumentException("property's declaring type must be equal to typeof(T).");
    }

    var constructedMethod = typeof (ObjectExtensions)
        .GetMethod("TryCast")
        .MakeGenericMethod(property.PropertyType);

    object valueToSet = null;
    var parameters = new[] {value, null};
    var tryCastSucceeded = Convert.ToBoolean(constructedMethod.Invoke(null, parameters));
    if (tryCastSucceeded)
    {
        valueToSet = parameters[1];
    }

    if (!property.CanAssignValue(valueToSet))
    {
        return;
    }
    property.SetValue(instance, valueToSet, null);
}

Y la extensión de los métodos para tratar con property.CanAssignValue...

public static bool CanAssignValue(this PropertyInfo p, object value)
{
    return value == null ? p.IsNullable() : p.PropertyType.IsInstanceOfType(value);
}

public static bool IsNullable(this PropertyInfo p)
{
    return p.PropertyType.IsNullable();
}

public static bool IsNullable(this Type t)
{
    return !t.IsValueType || Nullable.GetUnderlyingType(t) != null;
}

0voto

hs586sd46s Puntos 1

Gracias @LukeH
He cambiado un poco:

public static object convertToPropType(PropertyInfo property, object value)
{
    object cstVal = null;
    if (property != null)
    {
        Type propType = Nullable.GetUnderlyingType(property.PropertyType);
        bool isNullable = (propType != null);
        if (!isNullable) { propType = property.PropertyType; }
        bool canAttrib = (value != null || isNullable);
        if (!canAttrib) { throw new Exception("Cant attrib null on non nullable. "); }
        cstVal = (value == null || Convert.IsDBNull(value)) ? null : Convert.ChangeType(value, propType);
    }
    return cstVal;
}

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X