【问题标题】:Setting a property by reflection with a string value使用字符串值通过反射设置属性
【发布时间】:2010-11-08 12:22:15
【问题描述】:

我想通过反射设置一个对象的属性,其值为string。 因此,例如,假设我有一个Ship 类,其属性为Latitude,即double

这是我想做的:

Ship ship = new Ship();
string value = "5.5";
PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude");
propertyInfo.SetValue(ship, value, null);

照原样,这会引发ArgumentException

“System.String”类型的对象无法转换为“System.Double”类型。

如何根据propertyInfo 将值转换为正确的类型?

【问题讨论】:

  • 问您的问题:这是自定义 ORM 解决方案的一部分吗?

标签: c# reflection type-conversion propertyinfo setvalue


【解决方案1】:

您可以使用Convert.ChangeType() - 它允许您使用任何IConvertible 类型的运行时信息来更改表示格式。但是,并非所有转换都是可能的,如果您想支持非 IConvertible 类型的转换,您可能需要编写特殊情况逻辑。

相应的代码(没有异常处理或特殊情况逻辑)将是:

Ship ship = new Ship();
string value = "5.5";
PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude");
propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null);

【讨论】:

  • 查看下面的@AliKaraca 答案。这个和下面的一个都是快速和松散的,但可以为常见类型完成工作。
  • TryChangeTypeCanChangeType吗?
【解决方案2】:

正如其他几个人所说,您想使用Convert.ChangeType

propertyInfo.SetValue(ship,
    Convert.ChangeType(value, propertyInfo.PropertyType),
    null);

其实我建议你看看整个Convert Class

这个类和许多其他有用的类是System Namespace 的一部分。我发现每年左右扫描该名称空间以查看我错过了哪些功能很有用。试试看!

【讨论】:

  • OP 可能想要一般的答案,用于设置任何类型的属性,该属性具有明显的字符串转换。
  • 好点。我将编辑并指向真正的回答者,或者如果有人添加我所说的关于命名空间的其余部分的话,我会删除我的。
【解决方案3】:

我注意到很多人都在推荐Convert.ChangeType - 这在某些情况下确实有效,但是一旦您开始涉及nullable 类型,您就会开始收到InvalidCastExceptions

http://weblogs.asp.net/pjohnson/archive/2006/02/07/Convert.ChangeType-doesn_2700_t-handle-nullables.aspx

几年前编写了一个包装器来处理这个问题,但这也不完美。

http://weblogs.asp.net/pjohnson/archive/2006/02/07/Convert.ChangeType-doesn_2700_t-handle-nullables.aspx

【讨论】:

    【解决方案4】:

    我尝试了 LBushkin 的答案,效果很好,但它不适用于空值和可为空的字段。所以我把它改成了这样:

    propertyName= "Latitude";
    PropertyInfo propertyInfo = ship.GetType().GetProperty(propertyName);
    if (propertyInfo != null)
    {
         Type t = Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType;
         object safeValue = (value == null) ? null : Convert.ChangeType(value, t);
         propertyInfo.SetValue(ship, safeValue, null);
    }
    

    【讨论】:

    • 我不得不说谢谢,因为我遇到了这个案例,这是唯一的解决方案。谢谢~!
    【解决方案5】:

    您可以使用类型转换器(无错误检查):

    Ship ship = new Ship();
    string value = "5.5";
    var property = ship.GetType().GetProperty("Latitude");
    var convertedValue = property.Converter.ConvertFrom(value);
    property.SetValue(self, convertedValue);
    

    在组织代码方面,您可以创建一个kind-of mixin,这将产生如下代码:

    Ship ship = new Ship();
    ship.SetPropertyAsString("Latitude", "5.5");
    

    这可以通过以下代码实现:

    public interface MPropertyAsStringSettable { }
    public static class PropertyAsStringSettable {
      public static void SetPropertyAsString(
        this MPropertyAsStringSettable self, string propertyName, string value) {
        var property = TypeDescriptor.GetProperties(self)[propertyName];
        var convertedValue = property.Converter.ConvertFrom(value);
        property.SetValue(self, convertedValue);
      }
    }
    
    public class Ship : MPropertyAsStringSettable {
      public double Latitude { get; set; }
      // ...
    }
    

    MPropertyAsStringSettable 可以重复用于许多不同的类。

    您还可以创建自己的自定义 type converters 以附加到您的属性或类:

    public class Ship : MPropertyAsStringSettable {
      public Latitude Latitude { get; set; }
      // ...
    }
    
    [TypeConverter(typeof(LatitudeConverter))]
    public class Latitude { ... }
    

    【讨论】:

    • 您添加标记接口而不是仅使用object 有什么特别的原因吗?
    • 是的,标记接口用作添加扩展方法的占位符。使用object 会将扩展方法添加到所有类,这通常是不可取的。
    【解决方案6】:

    您可能正在寻找Convert.ChangeType 方法。例如:

    Ship ship = new Ship();
    string value = "5.5";
    PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude");
    propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null);
    

    【讨论】:

      【解决方案7】:

      使用Convert.ChangeType 并从PropertyInfo.PropertyType 获取要转换的类型。

      propertyInfo.SetValue( ship,
                             Convert.ChangeType( value, propertyInfo.PropertyType ),
                             null );
      

      【讨论】:

        【解决方案8】:

        我会用一个一般性的答案来回答这个问题。通常这些答案不适用于指导。这也是一个带有 guid 的工作版本。

        var stringVal="6e3ba183-89d9-e611-80c2-00155dcfb231"; // guid value as string to set
        var prop = obj.GetType().GetProperty("FooGuidProperty"); // property to be setted
        var propType = prop.PropertyType;
        
        // var will be type of guid here
        var valWithRealType = TypeDescriptor.GetConverter(propType).ConvertFrom(stringVal); 
        

        【讨论】:

        • 这应该是公认的答案。它也适用于 GUID
        • 但似乎仍然不适用于可空类型。
        【解决方案9】:

        或者你可以试试:

        propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null);
        
        //But this will cause problems if your string value IsNullOrEmplty...
        

        【讨论】:

          【解决方案10】:

          如果您正在编写 Metro 应用程序,您应该使用其他代码:

          Ship ship = new Ship();
          string value = "5.5";
          PropertyInfo propertyInfo = ship.GetType().GetTypeInfo().GetDeclaredProperty("Latitude");
          propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType));
          

          注意:

          ship.GetType().GetTypeInfo().GetDeclaredProperty("Latitude");
          

          而不是

          ship.GetType().GetProperty("Latitude");
          

          【讨论】:

            【解决方案11】:

            使用以下代码应该可以解决您的问题:

            item.SetProperty(prop.Name, Convert.ChangeType(item.GetProperty(prop.Name).ToString().Trim(), prop.PropertyType));
            

            【讨论】:

              【解决方案12】:

              您是想玩 Reflection 还是想构建一个生产软件?我会质疑您为什么要使用反射来设置属性。

              Double new_latitude;
              
              Double.TryParse (value, out new_latitude);
              ship.Latitude = new_latitude;
              

              【讨论】:

              • 你应该尊重人们试图做的事情,而不是你认为他们必须做的事情。否决。 (来自GenericProgramming.exe:ReflectionBenefits()
              • 呃,可能是因为您事先不知道该属性是什么,并且在键入时,您使用的值始终是字符串?这是我的情况:我正在屏幕抓取 HTML,所以我得到的值始终是一个字符串,我想要哪些属性以及如何找到它们是在配置文件中定义的,所以反射是唯一合理的方法。
              猜你喜欢
              • 2011-04-12
              • 1970-01-01
              • 2018-02-07
              • 2020-05-18
              • 2019-02-28
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-11-02
              相关资源
              最近更新 更多