通过反射设置属性值无法工作,而直接设置属性值在C#中有效。

huangapple go评论68阅读模式
英文:

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&lt;Type, Func&lt;object, object&gt;&gt; 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 =&gt; {
    TargetPropType result = (double)o; // assuming only doubles can get converted to TargetPropType
    return result;
    // or simply &quot;return new TargetPropType((double)o)&quot; if that&#39;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&lt;Type&gt;() { ... };
object value = theValueYouWantToSet;
if (!listOfPrimitives.Contains(propertyType)) {
    var method = propertyType.GetMethod(&quot;op_Implicit&quot;, 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.

huangapple
  • 本文由 发表于 2023年6月6日 16:01:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/76412549.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定