Search code examples
c#exceptionreflectioncastingproperties

Setting property value via reflection doesn't work while setting it directly works in C#


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?


Solution

  • 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.