通过反射使用字符串值设置属性


312

我想通过反射设置对象的属性,其值为type 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


1
您的问题:这是定制ORM解决方案的一部分吗?
user3308043 2014年

Answers:


527

您可以使用Convert.ChangeType()-它允许您使用任何IConvertible类型的运行时信息来更改表示形式格式。但是,并非所有转换都是可行的,并且如果您要支持来自not类型的转换,则可能需要编写特殊情况的逻辑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
Shimmy Weitzhandler,

34

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

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

实际上,我建议您查看整个ConvertClass

此类和许多其他有用的类是SystemNamespace的一部分。我发现每年大约扫描该名称空间以查看我错过的功能很有用。试试看!


1
OP可能需要通用的答案,即设置任何具有明显从字符串转换的类型的属性。
Daniel Earwicker,2009年

好点子。我将编辑并指向真正的应答者,或者如果有人将添加我对命名空间其余部分所说的内容,则将其删除。
约翰·桑德斯


13

我尝试了LBushkin的答案,但效果很好,但不适用于null值和可为空的字段。所以我将其更改为:

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

我遇到这个案子时不得不说谢谢,这是唯一的解决方案。谢谢〜!
弗朗瓦

11

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

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

就组织代码而言,您可以创建一种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 可以重用于许多不同的类。

您还可以创建自己的自定义类型转换器以附加到属性或类:

public class Ship : MPropertyAsStringSettable {
  public Latitude Latitude { get; set; }
  // ...
}

[TypeConverter(typeof(LatitudeConverter))]
public class Latitude { ... }

是否有特定的原因为什么要添加标记insterface而不是仅仅使用object
Groo

1
是的,标记接口用作占位符,以向其添加扩展方法。使用object会将扩展方法添加到所有类,这通常是不希望的。
Jordão酒店

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

5

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

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

4

我将用一个一般性的答案来回答。通常,这些答案不适用于guid。这也是带有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); 

1
这应该是公认的答案。它也可用于<3的GUID。谢谢,阿里(这是我女儿的小名)
克特林Rădoi

3

或者您可以尝试:

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

//But this will cause problems if your string value IsNullOrEmplty...

2

如果您正在编写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");

0

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

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

-9

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

Double new_latitude;

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

1
您应该尊重人们试图做的事情,而不是您认为他们必须做的事情。不赞成投票。(从GenericProgramming.exe:ReflectionBenefits()
ПетърПетров
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.