web-dev-qa-db-fra.com

Problème de conversion de type lors de la définition d'une propriété par réflexion

Nous avons une propriété de type long? qui est remplie par un int.

Cela fonctionne bien lorsque je viens de définir directement la propriété obj.Value = v;, mais lorsque j'essaie de définir la propriété par réflexion info.SetValue(obj, v, null);, cela me donne l'exception suivante:

Les objets de type 'System.Int32' ne peuvent pas être convertis en type 'System.Nullable`1 [System.Int64]'.

Ceci est un scénario simplifié:

    class TestClass
    {
        public long? Value { get; set; }
    }

    [TestMethod]
    public void TestMethod2()
    {
        TestClass obj = new TestClass();
        Type t = obj.GetType();

        PropertyInfo info = t.GetProperty("Value");
        int v = 1;

        // This works
        obj.Value = v;

        // This does not work
        info.SetValue(obj, v, null);
    }

Pourquoi cela ne fonctionne-t-il pas lorsque vous définissez la propriété via reflection alors que cela fonctionne lorsque vous définissez la propriété directement?

23
Shikyo

Consultez l'article complet: Comment définir la valeur d'une propriété à l'aide de Reflection?

code complet si vous définissez la valeur pour le type nullable 

public static void SetValue(object inputObject, string propertyName, object propertyVal)
{
    //find out the type
    Type type = inputObject.GetType();

    //get the property information based on the type
    System.Reflection.PropertyInfo propertyInfo = type.GetProperty(propertyName);

    //find the property type
    Type propertyType = propertyInfo.PropertyType;

    //Convert.ChangeType does not handle conversion to nullable types
    //if the property type is nullable, we need to get the underlying type of the property
    var targetType = IsNullableType(propertyType) ? Nullable.GetUnderlyingType(propertyType) : propertyType;

    //Returns an System.Object with the specified System.Type and whose value is
    //equivalent to the specified object.
    propertyVal = Convert.ChangeType(propertyVal, targetType);

    //Set the value of the property
    propertyInfo.SetValue(inputObject, propertyVal, null);

}
private static bool IsNullableType(Type type)
{
    return type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>));
}

vous devez convertir une valeur comme ceci i.e vous devez convertir une valeur en votre type de propriété comme ci-dessous 

PropertyInfo info = t.GetProperty("Value");
object value = null;
try 
{ 
    value = System.Convert.ChangeType(123, 
        Nullable.GetUnderlyingType(info.PropertyType));
} 
catch (InvalidCastException)
{
    return;
}
propertyInfo.SetValue(obj, value, null);

vous devez le faire car vous ne pouvez convertir aucune valeur arbitraire en un type donné ... vous devez donc le convertir comme ceci 

59
Pranay Rana

Quand tu écris:

obj.Value = v;

le compilateur sait comment faire le bon casting pour vous et compile réellement

obj.Value = new long?((long) v);

Lorsque votre réflexion sur l'utilisation, aucun compilateur ne peut vous aider.

3
fsimonazzi

Parce que le type long a une méthode de conversion implicite.

6.1.2 Conversions numériques implicites

Vous pouvez voir la méthode de conversion implicite comme une méthode cachée qui existe derrière le symbole =.

Cela fonctionne aussi avec le type nullable:

int i = 0;
int? j = i; // Implicit conversion
long k = i; // Implicit conversion
long? l = i; // Implicit conversion

Mais l'inverse ne fonctionne pas, car il n'existe pas de conversion implicite pour passer un null à un non-null:

int? i = 0;
int j = i; // Compile assert. An explicit conversion exit... 
int k = (int)i; // Compile, but if i is null, you will assert at runtime.

Vous n'êtes pas obligé de convertir explicitement une int en un int?... ou un long?.

Toutefois, lorsque vous utilisez la réflexion, vous ignorez la conversion implicite et affectez directement la valeur à la propriété. De cette façon, vous devez le convertir explicitement.

info.SetValue(obj, (long?)v, null);

Reflection ignore toutes les choses cachées derrière =.

2
LightStriker

J'ai également rencontré ce problème lors de la création de nouveaux objets de manière réfléchie.

Voici comment ne pas le faire:

  var newOne = Activator.CreateInstance(srcProp.GetType());

  srcProp.SetValue(newGameData, newOne, null);

Et voici comment le faire:

  var newOne = Activator.CreateInstance(srcProp.PropertyType);

  srcProp.SetValue(newGameData, newOne, null);
0
DShook

Consolider et développer la réponse de Pranay Rana à gérer enums comme suggéré par SilverNinja (et à répondre par thecoop ) et mettre les connaissances acquises au même endroit. C'est un peu plus de copier/coller .;

private void SetApiSettingValue(object source, string propertyName, object valueToSet)
{
    // find out the type
    Type type = source.GetType();

    // get the property information based on the type
    System.Reflection.PropertyInfo property = type.GetProperty(propertyName);

    // Convert.ChangeType does not handle conversion to nullable types
    // if the property type is nullable, we need to get the underlying type of the property
    Type propertyType = property.PropertyType;
    var targetType = IsNullableType(propertyType) ? Nullable.GetUnderlyingType(propertyType) : propertyType;

    // special case for enums
    if (targetType.IsEnum)
    {
        // we could be going from an int -> enum so specifically let
        // the Enum object take care of this conversion
        if (valueToSet != null)
        {
            valueToSet = Enum.ToObject(targetType, valueToSet);
        }
    }
    else
    {
        // returns an System.Object with the specified System.Type and whose value is
        // equivalent to the specified object.
        valueToSet = Convert.ChangeType(valueToSet, targetType);
    }

    // set the value of the property
    property.SetValue(source, valueToSet, null);
}

private bool IsNullableType(Type type)
{
    return type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>));
}
0
DiskJunky