英文:
Setting property value via reflection doesn't work while setting it directly works in C#
问题
以下是翻译好的部分:
我正在尝试通过C#中的反射设置一个框架的类型为TargetPropType
的属性TargetProp
的值。
该属性似乎是一个包含双精度数的容器,用于将双精度数与单位一起使用。
以下操作有效:
parentOfProperty.TargetProp = 5.0;
而以下操作失败,并出现System.ArgumentException异常,消息为“无法将类型为'System.Double'的对象转换为类型'TargetPropType'”。
PropertyInfo setProperty = parentOfProperty.GetType().GetProperty("TargetProp");
setProperty.SetValue(parentOfProperty, 5.0, null);
以下操作也失败,并出现相同的异常:
setProperty.GetSetMethod()?.Invoke(parentOfProperty, new object[] { 5.0 });
第一种赋值方式成功的原因可能与反射赋值失败的方式有所不同,您想知道为什么反射赋值失败。
英文:
I'm trying to set the value of a property TargetProp
of type TargetPropType
of a framework via reflection in C#.
The property seems to be a container for doubles which enrich the double with units.
Doing the following works
parentOfProperty.TargetProp = 5.0;
While the following fails with a System.ArgumentException "Object of type 'System.Double' cannot be converted to type 'TargetPropType'."
PropertyInfo setProperty = parentOfProperty.GetType().GetProperty("TargetProp");
setProperty.SetValue(parentOfProperty, 5.0, null);
The following also fails with the same exception
setProperty.GetSetMethod()?.Invoke(parentOfProperty, new object[] { 5.0 });
What could be different in the first assignment that it works while the reflection assignments fail?
答案1
得分: 2
直接将属性设置为 double
类型是有效的,但使用反射则不行,因为属性的类型具有从 int
进行隐式转换的操作,正如您在评论中链接的内容所述。反射无法解析隐式转换操作符。
因此,您可以手动调用隐式转换操作符,然后将结果传递给 SetValue
:
TargetPropType arg = 5.0;
setProperty.SetValue(parentOfProperty, arg, null);
然而,似乎您想要一个更通用的解决方案。根据评论:
> 该程序读取输入文件和转换文件。TargetProp
由转换文件给出,可以是框架中的任何属性。在运行时,程序只知道某个值必须转换为 TargetProp
。
我建议使用 Dictionary<Type, Func<object, object>>
记录如何创建每种属性类型的方式。让我们称之为 conversionsDict
。例如,conversionsDict
将具有一个条目,其中键是 typeof(TargetPropType)
,而值是类似以下内容的东西:
o => {
TargetPropType result = (double)o; // 假设只有 double 可以转换为 TargetPropType
return result;
// 或者如果隐式转换执行的是这样的操作,也可以直接返回 "return new TargetPropType((double)o)"
}
您可以将所有需要进行转换的类型放入 conversionsDict
中,然后执行以下操作:
PropertyInfo setProperty = parentOfProperty.GetType().GetProperty(someStringYouGetFromTransformationFile);
Type propertyType = setProperty.PropertyType;
object value = theValueYouWantToSet;
if (conversionsDict.TryGetValue(propertyType, out var converter)) {
value = converter(value);
}
setProperty.SetValue(parentOfProperty, value, null);
或者,您可以使用反射来调用隐式转换操作符。该操作符编译为名为 op_Implicit
的 IL 方法,尽管我不确定这是否在任何地方有明确定义。
// 枚举不需要进行转换的原始类型
var listOfPrimitives = new List<Type>() { ... };
object value = theValueYouWantToSet;
if (!listOfPrimitives.Contains(propertyType)) {
var method = propertyType.GetMethod("op_Implicit", new[] { value.GetType() });
if (method != null) {
value = method.Invoke(null, new[] { value });
}
}
setProperty.SetValue(parentOfProperty, value, null);
还可以参考此帖子,其中使用了 TypeDescriptor
API。
英文:
Setting the property directly to double
works but using reflection doesn't, because the type of the property has an implicit conversion from int
, as you linked in the comments. Reflection doesn't resolve implicit conversion operators.
So you can just call the implicit conversion operator manually, and pass the result to SetValue
:
TargetPropType arg = 5.0;
setProperty.SetValue(parentOfProperty, arg, null);
However it seems like you want a more general solution. From the comments:
> The program reads an input file and a transformation file. The TargetProp
is given by the transformation file and can be any property in the framework. At runtime the program only knows that some value must be converted to TargetProp
.
I would recommend using a Dictionary<Type, Func<object, object>>
to record exactly how to create each type of property that you may encounter. Let's call this conversionsDict
. For example, conversionsDict
would have an entry where the key is typeof(TargetPropType)
, and the value is something like:
o => {
TargetPropType result = (double)o; // assuming only doubles can get converted to TargetPropType
return result;
// or simply "return new TargetPropType((double)o)" if that's what the implicit conversion does
}
You can put all the types that require a conversion in conversionsDict
, and do:
PropertyInfo setProperty = parentOfProperty.GetType().GetProperty(someStringYouGetFromTransformationFile);
Type propertyType = setProperty.PropertyType;
object value = theValueYouWantToSet;
if (conversionsDict.TryGetValue(propertyType, out var converter)) {
value = converter(value);
}
setProperty.SetValue(parentOfProperty, value, null);
Alternatively, you can use reflection to call the implicit conversion operator. The operator compiles to a method in IL called op_Implicit
, though I'm not sure if this is specified anywhere.
// list out the primitive types that needs no conversion
var listOfPrimitives = new List<Type>() { ... };
object value = theValueYouWantToSet;
if (!listOfPrimitives.Contains(propertyType)) {
var method = propertyType.GetMethod("op_Implicit", new[] { value.GetType() });
if (method != null) {
value = method.Invoke(null, new[] { value });
}
}
setProperty.SetValue(parentOfProperty, value, null);
See also this post which uses the TypeDescriptor
API.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论