'casting' with reflection

55,164

Solution 1

void SetValue(PropertyInfo info, object instance, object value)
{
    info.SetValue(instance, Convert.ChangeType(value, info.PropertyType));
}

Solution 2

Thomas answer only works for types that implement IConvertible interface:

For the conversion to succeed, value must implement the IConvertible interface, because the method simply wraps a call to an appropriate IConvertible method. The method requires that conversion of value to conversionType be supported.

This code compile a linq expression that does the unboxing (if needed) and the conversion:

    public static object Cast(this Type Type, object data)
    {
        var DataParam = Expression.Parameter(typeof(object), "data");
        var Body = Expression.Block(Expression.Convert(Expression.Convert(DataParam, data.GetType()), Type));

        var Run = Expression.Lambda(Body, DataParam).Compile();
        var ret = Run.DynamicInvoke(data);
        return ret;
    }

The resulting lambda expression equals to (TOut)(TIn)Data where TIn is the type of the original data and TOut is the given type

Solution 3

The answer by Thomas is right, but I thought I would add my finding that Convert.ChangeType does not handle conversion to nullable types. To handle nullable types, I used the following code:

void SetValue(PropertyInfo info, object instance, object value)
{
    var targetType = info.PropertyType.IsNullableType() 
         ? Nullable.GetUnderlyingType(info.PropertyType) 
         : info.PropertyType; 
    var convertedValue = Convert.ChangeType(value, targetType);

    info.SetValue(instance, convertedValue, null);
}

This code makes use of the following extension method:

public static class TypeExtensions
{
    public static bool IsNullableType(this Type type)
    {
        return type.IsGenericType
               && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>));
    }
}

Solution 4

Contributing to jeroenh's answer, I would add that Convert.ChangeType crashes with a null value, so the line for getting the converted value should be:

var convertedValue = value == null ? null : Convert.ChangeType(value, targetType);

Solution 5

When the Type is a Nullable Guid then none of the above proposed solutions work. Invalid cast from 'System.DBNull' to 'System.Guid' exception is thrown at Convert.ChangeType

To fix that change to:

var convertedValue = value == System.DBNull.Value ? null : Convert.ChangeType(value, targetType);
Share:
55,164
jeroenh
Author by

jeroenh

Technical Architect &amp; Software Engineer

Updated on August 23, 2022

Comments

  • jeroenh
    jeroenh over 1 year

    Consider the following sample code:

    class SampleClass
    {
        public long SomeProperty { get; set; }
    }
    
    public void SetValue(SampleClass instance, decimal value)
    {
        // value is of type decimal, but is in reality a natural number => cast
        instance.SomeProperty = (long)value;
    }
    

    Now I need to do something similar through reflection:

    void SetValue(PropertyInfo info, object instance, object value)
    {
        // throws System.ArgumentException: Decimal can not be converted to Int64
        info.SetValue(instance, value)  
    }
    

    Note that I cannot assume that the PropertyInfo always represents a long, neither that value is always a decimal. However, I know that value can be casted to the correct type for that property.

    How can I convert the 'value' parameter to the type represented by PropertyInfo instance through reflection ?

  • jeroenh
    jeroenh about 11 years
    This problem is not specific to Guid but rather due to the fact that you get DBNull.Value instead of simply null when fetching null values from the database through ADO.Net. You will see the same with nullable int, for example.
  • jnm2
    jnm2 over 9 years
    This is actually the answer I came looking for. Non-IConvertible dynamic casting.
  • jnm2
    jnm2 over 9 years
    Heh I would- if I was OP.
  • derekantrican
    derekantrican over 3 years
    Note that Convert.ChangeType(value, property.PropertyType); can still fail if value does not implement the IConvertible interface. For instance, if info.PropertyType is some IEnumerable
  • derekantrican
    derekantrican over 3 years
    Was hoping this would save me when trying to cast IEnumerable<object> (where those objects are strings) to IEnumerable<string>. Unfortunately I'm getting errors like Unable to cast object of type 'System.Collections.Generic.IEnumerable'1[System.Object]' to type 'System.Collections.Generic.IEnumerable'1[System.String]'.
  • shtse8
    shtse8 about 3 years
    @derekantrican you need to iterate the list and cast each by your self.
  • DDRider62
    DDRider62 about 2 years
    I know this question is old, but also notice that Convert.ChangeType can return a different value than plain casting. For example, double d = 5.57293; int i = Convert(d, typeof(int)) will return 6. But doing int i = (int)d will return 5.