通过reflection设置属性时键入转换问题

我们有一个long?类型的属性long? 充满了int

当我直接设置属性obj.Value = v;时,这工作正常obj.Value = v; 但是当我尝试通过reflection信息设置属性时info.SetValue(obj, v, null); 它给了我一个以下例外:

‘System.Int32’类型的对象无法转换为’System.Nullable`1 [System.Int64]’类型。

这是一个简化的场景:

  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); } 

为什么在直接设置属性时通过reflection设置属性时不起作用?

查看全文: 如何使用Reflection设置属性的值?

如果要为可空类型设置值,则为完整代码

 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<>)); } 

你需要转换这样的值,即你需要将值转换为你的属性类型,如下所示

 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); 

你需要这样做,因为你不能将任何arbirtary值转换为给定类型…所以你需要像这样转换它

当你写:

 obj.Value = v; 

编译器知道如何为您进行正确的转换并实际编译

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

当您使用reflection时,没有编译器可以帮助您。

因为long类型有一个隐式转换方法。

6.1.2隐式数字转换

您可以将隐式转换方法视为=符号后面的隐藏方法。

它也适用于可空类型:

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

但是反过来不起作用,因为没有隐式转换将null传递给非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. 

您不必显式将int转换为int? ……还是long?

但是,当您使用reflection时,您将绕过隐式转换并直接将值分配给属性。 这样,您必须明确地转换它。

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

reflection跳过隐藏在背后的所有甜蜜的东西=

根据SilverNinja (并由thecoop回答)建议,巩固和扩展Pranay Rana处理enum的答案,并将学习积累在一个地方。 这是一个更复制/可爱的。

 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<>)); } 

在reflection创建新对象时,我也遇到了这个问题。

这是不怎么做:

  var newOne = Activator.CreateInstance(srcProp.GetType()); srcProp.SetValue(newGameData, newOne, null); 

这是怎么做的:

  var newOne = Activator.CreateInstance(srcProp.PropertyType); srcProp.SetValue(newGameData, newOne, null);