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?
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
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.
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 =
.
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);
Consolider et développer la réponse de Pranay Rana à gérer enum
s 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<>));
}