关于c#:使用字符串值通过反射设置属性

Setting a property by reflection with a string value

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

我想做的是:

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

同样,这会抛出一个ArgumentException

Object of type 'System.String' cannot be converted to type 'System.Double'.

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


您可以使用Convert.ChangeType()—它允许您使用任何IConvertible类型的运行时信息来更改表示格式。不过,并不是所有的转换都是可能的,如果您希望支持来自非IConvertible类型的转换,则可能需要编写特殊的案例逻辑。

相应的代码(无异常处理或特殊情况逻辑)为:

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


正如其他一些人所说,您希望使用Convert.ChangeType

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

事实上,我建议你看看整个Convert类。

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


我注意到很多人都在推荐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


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

1
2
3
4
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);

在组织代码方面,您可以创建一种mixin,它将导致如下代码:

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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可用于许多不同的类。

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

1
2
3
4
5
6
7
public class Ship : MPropertyAsStringSettable {
  public Latitude Latitude { get; set; }
  // ...
}

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


我尝试了lbushkin的答案,它工作得很好,但对于空值和可空字段不起作用。所以我把它改成了:

1
2
3
4
5
6
7
8
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);
}


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

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


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

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

我将用一般性的回答来回答这个问题。通常这些答案不适用于guid。这里还有一个使用guid的工作版本。

1
2
3
4
5
6
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
2
3
propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null);

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

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

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

注:

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

而不是

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

你是想玩弄反射,还是想构建一个生产软件?我会问你为什么用反射来设置属性。

1
2
3
4
Double new_latitude;

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