【发布时间】:2019-06-15 04:31:37
【问题描述】:
我正在尝试创建一个库,该库将从 Web 服务中提取并将检索到的值映射到用户提供的任何类,使用属性来装饰目标类属性。这适用于基本类型,但是,某些“类型”是来自另一个进行单位转换的库的自定义类型(ConvertableDecimal、...Int、...Float)。数值存储在称为“BaseValue”类型的公共属性中。
以下是实现这些类型的属性的类的属性的示例:
[OEDProperty("Discharge Capacity Rated", "BaseValue")]
public ConvertableDecimal DischargeCapacity { get; set; } = new ConvertableDecimal(MeasureType.FlowRate);
“OEDProperty”是我创建的用于装饰属性的属性类,它需要两个输入:
- 要映射的 xml 字段名(例如“额定放电容量”)和
- 在这种情况下称为“TargetMember”、“BaseValue”的可选参数...
这里是映射方法:
public static T Map<T> (OEDData OED, out string Errors)
{
string mappingErrors = string.Empty;
object retObj = Activator.CreateInstance (typeof (T)); //we'll reset this later if we need to, e.g. targeting a member
PropertyInfo[] properties = retObj.GetType ().GetProperties();
foreach (PropertyInfo pi in properties)
{
OEDPropertyAttribute propAtt = (OEDPropertyAttribute) pi.GetCustomAttribute (typeof (OEDPropertyAttribute));
if (propAtt != null)
{
PropertyInfo piTargetMember = null;
if (!string.IsNullOrEmpty (propAtt.TargetMember))
{
try
{
piTargetMember = pi.PropertyType.GetProperty (propAtt.TargetMember);
}
catch (Exception ex)
{
mappingErrors += string.Format("Error locating target member \"{0}\" for type \"{1}\" when setting field \"{2}\".\r\nMake sure the target member name is spelled correctly. Target member names ARE case sensitive.\r\nError: {3}",
propAtt.TargetMember,
propAtt.GetType().Name,
propAtt.Field.ToLower(),
ex.Message);
}
}
if (propAtt.IsHeaderField) //header fields
{
/*snip*/
}
else //fields
{
try
{
var fVal = OED.Fields.FirstOrDefault (f => f.FieldName.ToLower () == propAtt.Field.ToLower ()).Value;
var convertedFVal = (piTargetMember == null) ? ChangeType (fVal, pi.PropertyType) : ChangeType (fVal, piTargetMember.PropertyType);
if (piTargetMember == null)
{
pi.SetValue(retObj, convertedFVal);
}
else
{
pi.SetValue(retObj.GetType().GetProperty(propAtt.TargetMember), convertedFVal);
//error happens here
//error text: Non-static method requires a target
}
}
catch (Exception ex)
{
mappingErrors += string.Format("Unable to map oed field value: \"{0}\".\r\nError: {1}", propAtt.Field.ToLower (), ex.Message);
}
}
}
}
Errors = mappingErrors;
return (T) retObj;
}
尝试设置属性值时的错误文本是:“非静态方法需要一个目标”
我从这篇文章 (Non-static method requires a target) 了解到这是由于运行时的空引用。
我的问题是,我有哪些选项可以让这个库正常工作,并且可以灵活处理未来可能出现的任何用户定义类型。
非常感谢任何见解。
【问题讨论】:
-
我花了一段时间才发现,因为格式太奇怪了,但是由于您传递给
SetValue的第一个参数为 null 并且您有一个非静态属性,所以会发生该错误。话虽如此,我发现整个pi.SetValue(retObj.GetType().GetProperty(propAtt.TargetMember), convertedFVal)的事情很可疑,因为我怀疑你想将属性本身作为目标传递,对吧? -
我认为这是我不理解的事情......对于一个简单的类型(字符串,十进制,int,float......)如果我这样做: pi.SetValue(retObj, convertFVal) ;然后,值设置得很好... ConvertableDecimal 是我在单位转换库中创建的一个类,并且有一个名为“BaseValue”(浮点数?)的属性被设置为一个共同商定的数字单位集,然后呈现给用户选择单位(例如英寸/毫米,加仑/升......等)所以,我需要能够填充“ConvertableDecimal”类/类型的“BaseValue”属性
-
prop.SetValue(target, value)是等价于target.prop = value的反射。因此,如果属性不是静态的,为什么你需要一个目标。GetProperty()给你一个属性,而不是一个值:你需要调用GetValue()(可能还传递一个有效的目标)来获取它的值. -
所以,使用上面的约定;我认为我真正想要的是“target.prop.BaseValue = value”它试图设置我难倒的“MyClass”类的“ConvertableDecimal”(类型)属性类型的“BaseValue”属性。它更深一层。如果“prop”是一个字符串,我说“target.prop =“Hello””,那么它每次都有效,但是,我没有得到一个属性的属性。 MyClass > MyProperty (typeof(ConvertableDecimal) > BaseValue = 123.456
标签: c# reflection attributes