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

359
我想通过反射设置一个对象的属性,并给它赋一个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
12个回答

592

您可以使用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的答案。这个和下面的一个都很快速,但对于常见类型来说都能胜任。 - Aaron Hudon
1
TryChangeType或者CanChangeType吗? - Shimmy Weitzhandler

36

正如其他人所说,你需要使用Convert.ChangeType

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

事实上,我建议您查看整个Convert
这个类和许多其他有用的类是System命名空间的一部分。我发现每年扫描该命名空间以查看我错过了哪些功能非常有用。试试吧!

1
OP可能想要一般性的答案,用于设置任何类型的属性,该属性从字符串中具有明显的转换。 - Daniel Earwicker
好的,我会编辑并指向真正的答案提供者,如果有人添加了我关于命名空间其余部分的内容,我会删除我的回答。 - John Saunders

27

我尝试了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);
}

我必须说感谢,因为我遇到了这个问题,而这是唯一的解决方案。谢谢~! - Franva

20

12

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

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 { ... }

你为什么要添加标记接口而不是直接使用“object”? - vgru
1
是的,标记接口充当占位符,用于添加扩展方法。使用 object 会将扩展方法添加到所有类中,这通常是不希望的。 - Jordão

8

你可能正在寻找 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 );

5

我会给出一个通用答案。通常这些答案不适用于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
这应该是被接受的答案。它也适用于GUIDs <3。谢谢,阿里(那是我女儿的昵称) - Cătălin Rădoi
但似乎仍无法处理可空类型。 - Auspex

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接