ChatGPT解决这个技术问题 Extra ChatGPT

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

我想通过反射设置一个对象的属性,其值为 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 解决方案的一部分吗?

I
Irshad

您可以使用 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 吗?
I
Irshad

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

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

事实上,我建议您查看整个 Convert Class

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


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

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

当我遇到这种情况时,我不得不说谢谢,这是唯一的解决方案。谢谢~!
I
Irshad

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


J
Jordão

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

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 会将扩展方法添加到所有类,这通常是不可取的。
J
John Calsbeek

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

I
Irshad

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

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

A
Ali Karaca

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

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 <3。谢谢,阿里(这是我女儿的昵称)
但似乎仍然不适用于可为空的类型。
b
bytebender

或者你可以试试:

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

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

S
Serhiy

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

K
Karl

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

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

c
clemahieu

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

Double new_latitude;

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

你应该尊重人们试图做的事情,而不是你认为他们必须做的事情。否决。 (来自 GenericProgramming.exe:ReflectionBenefits()
呃,也许是因为您事先不知道 Property 是什么,并且在键入时,您使用的值始终是字符串?这是我的情况:我正在屏幕抓取 HTML,所以我得到的值始终是一个字符串,我想要哪些属性以及如何找到它们是在配置文件中定义的,所以反射是唯一合理的方法。