【问题标题】:Type conversion issue when setting property through reflection通过反射设置属性时的类型转换问题
【发布时间】:2021-11-16 13:58:59
【问题描述】:

我们有一个long? 类型的属性,它被int 填充。

当我直接设置属性obj.Value = v; 时,这工作正常,但是当我尝试通过反射设置属性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设置属性时不起作用而直接设置属性时起作用?

【问题讨论】:

    标签: c# reflection


    【解决方案1】:

    查看全文:How to set value of a property using 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);
    

    您需要这样做,因为您无法将任何任意值转换为给定类型...所以您需要像这样转换它

    【讨论】:

    • 抱歉耽误了您的代码示例。这成功了,谢谢!
    • 设置'null'时不起作用。很容易解决,尝试编辑您的帖子但被拒绝了。
    • @PranayRana 刚刚找到了这个解决方案并实施到我遇到的问题中。效果很好,我所做的唯一更改就是将您的助手变成扩展。
    • 这在Guids上也很奇怪
    • 对于targetType,我使用这种方法:var targetType = Nullable.GetUnderlyingType(propType) ??道具类型;
    【解决方案2】:

    当你写作时:

    obj.Value = v;
    

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

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

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

    【讨论】:

      【解决方案3】:

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

      6.1.2 Implicit numeric conversions

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

      它也适用于可空类型:

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

      但是反过来是行不通的,因为不存在将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?

      但是,当您使用反射时,您会绕过隐式转换并将值直接分配给属性。这样,您必须显式转换它。

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

      反思跳过隐藏在=后面的所有甜蜜的东西。

      【讨论】:

      • 怀疑是这样的,感谢清晰的解释。
      【解决方案4】:

      按照SilverNinja 的建议(并由thecoop 回答)巩固和扩展Pranay Rana 的回答以处理enums,并将积累的知识集中在一个地方。这是更多的复制/粘贴。;

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

      【讨论】:

      • 我正在寻找如何转换为枚举。谷歌把我带到了这个页面,所以我很高兴你插话!!!像魅力一样工作。
      【解决方案5】:

      我在反射式创建新对象时也遇到了这个问题。

      这是怎么做的:

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

      这是怎么做的:

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

      【讨论】:

        【解决方案6】:

        这是旧线程。但是此页面中的解决方案不起作用。我做了一些调整并且效果很好(在 .netcore 2.0 中)!

        obj = Activator.CreateInstance<T>();
        foreach (PropertyInfo prop in obj.GetType().GetProperties())
        {
            if (!object.Equals(this.reader[prop.Name], DBNull.Value))
            {
                if (prop.PropertyType.Name.Contains("Nullable"))
                {
                    prop.SetValue(obj, Convert.ChangeType(this.reader[prop.Name], Nullable.GetUnderlyingType(prop.PropertyType)), null);
                }
                else
                {
                    prop.SetValue(obj, this.reader[prop.Name], null);
                }
            }
        }
        

        【讨论】:

          【解决方案7】:

          var 属性 = propTypes.GetProperty(attribute);

          TypeCode typeCode = Type.GetTypeCode(properties.PropertyType);

          switch (typeCode)
          {
            case TypeCode.Int32:
            properties.SetValue(m, Convert.ToInt32(value.AsPrimitive().Value));
            break;
            case TypeCode.Int64:
            properties.SetValue(m, Convert.ToInt64(value.AsPrimitive().Value));
            break;
          }
          

          【讨论】:

            【解决方案8】:

            您可以尝试这样的方法,它对我的​​转换有用吗?:

            Type propType = propInfo.PropertyType;
            
            if (propType.AssemblyQualifiedName.StartsWith("System.Nullable`1[[System.Double"))
            {
                if (dataRow[column.ColumnName] == DBNull.Value)
                {
                    propInfo.SetValue(obj, null, null);
                }
                else
                    propInfo.SetValue(obj, (double?)dataRow[column.ColumnName], null);
            }
            else
                propInfo.SetValue(obj, dataRow[column.ColumnName], null);
            

            【讨论】:

              猜你喜欢
              • 2016-12-30
              • 2019-02-28
              • 2010-10-26
              • 1970-01-01
              • 1970-01-01
              • 2018-02-07
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多