31

We have a property of type long? that gets filled with an int.

This works fine when i just set the property directly obj.Value = v; but when i try and set the property through reflection info.SetValue(obj, v, null); it gives me a the following exception:

Object of type 'System.Int32' cannot be converted to type 'System.Nullable`1[System.Int64]'.

This is a simplified scenario:

    class TestClass
    {
        public long? Value { get; set; }
    }

    [TestMethod]
    public void TestMethod2()
    {
        TestClass obj = new TestClass();
        Type t = obj.GetType();

        PropertyInfo info = t.GetProperty("Value");
        int v = 1;

        // This works
        obj.Value = v;

        // This does not work
        info.SetValue(obj, v, null);
    }

Why does it not work when setting the property through reflection while it works when setting the property directly?

Pranay Rana
  • 175,020
  • 35
  • 237
  • 263
Shikyo
  • 1,430
  • 1
  • 14
  • 25

8 Answers8

77

Check full article : How to set value of a property using Reflection?

full code if you are setting value for nullable type

public static void SetValue(object inputObject, string propertyName, object propertyVal)
{
    //find out the type
    Type type = inputObject.GetType();

    //get the property information based on the type
    System.Reflection.PropertyInfo propertyInfo = type.GetProperty(propertyName);

    //find the property type
    Type propertyType = propertyInfo.PropertyType;

    //Convert.ChangeType does not handle conversion to nullable types
    //if the property type is nullable, we need to get the underlying type of the property
    var targetType = IsNullableType(propertyType) ? Nullable.GetUnderlyingType(propertyType) : propertyType;

    //Returns an System.Object with the specified System.Type and whose value is
    //equivalent to the specified object.
    propertyVal = Convert.ChangeType(propertyVal, targetType);

    //Set the value of the property
    propertyInfo.SetValue(inputObject, propertyVal, null);

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

you need to convert value like this i.e you need to convert value to your property type like as below

PropertyInfo info = t.GetProperty("Value");
object value = null;
try 
{ 
    value = System.Convert.ChangeType(123, 
        Nullable.GetUnderlyingType(info.PropertyType));
} 
catch (InvalidCastException)
{
    return;
}
propertyInfo.SetValue(obj, value, null);

you need to do this because you cannot convert any arbirtary value to given type...so you need to convert it like this

DiskJunky
  • 4,750
  • 3
  • 37
  • 66
Pranay Rana
  • 175,020
  • 35
  • 237
  • 263
4

When you write:

obj.Value = v;

the compiler knows how to do the proper casting for you and actually compiles

obj.Value = new long?((long) v);

When your use reflection there is no compiler to help you.

fsimonazzi
  • 2,975
  • 15
  • 13
2

Because the type long has an implicit conversion method.

6.1.2 Implicit numeric conversions

You can see implicit conversion method as hidden method that exist behind the = symbol.

It also work with nullable type:

int i = 0;
int? j = i; // Implicit conversion
long k = i; // Implicit conversion
long? l = i; // Implicit conversion

But going the other way around doesn't work, because no implicit conversion exist to pass a null to a non-null:

int? i = 0;
int j = i; // Compile assert. An explicit conversion exit... 
int k = (int)i; // Compile, but if i is null, you will assert at runtime.

You don't have to explicitly convert a int to a int?... or a long?.

However, when you use reflection, you are bypassing implicit conversion and assign directly the value to the property. This way, you have to convert it explicitly.

info.SetValue(obj, (long?)v, null);

Reflection skip all the sweet stuff hidden behind =.

Pranay Rana
  • 175,020
  • 35
  • 237
  • 263
LightStriker
  • 19,738
  • 3
  • 23
  • 27
2

Consolidating and expanding on Pranay Rana's answer to handle enums as suggested by SilverNinja (and answered by thecoop) and put accumulated the learnings in one place. This is a little more copy/pastable.;

private void SetApiSettingValue(object source, string propertyName, object valueToSet)
{
    // find out the type
    Type type = source.GetType();

    // get the property information based on the type
    System.Reflection.PropertyInfo property = type.GetProperty(propertyName);

    // Convert.ChangeType does not handle conversion to nullable types
    // if the property type is nullable, we need to get the underlying type of the property
    Type propertyType = property.PropertyType;
    var targetType = IsNullableType(propertyType) ? Nullable.GetUnderlyingType(propertyType) : propertyType;

    // special case for enums
    if (targetType.IsEnum)
    {
        // we could be going from an int -> enum so specifically let
        // the Enum object take care of this conversion
        if (valueToSet != null)
        {
            valueToSet = Enum.ToObject(targetType, valueToSet);
        }
    }
    else
    {
        // returns an System.Object with the specified System.Type and whose value is
        // equivalent to the specified object.
        valueToSet = Convert.ChangeType(valueToSet, targetType);
    }

    // set the value of the property
    property.SetValue(source, valueToSet, null);
}

private bool IsNullableType(Type type)
{
    return type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>));
}
DiskJunky
  • 4,750
  • 3
  • 37
  • 66
  • 1
    I was looking for how to convert to enum. Google brought me to this page, so I am glad you chimed in!!! Works like a charm. – Iannazzi Dec 11 '19 at 15:57
0

I also ran into this problem when creating new objects reflectively.

This is how not to do it:

  var newOne = Activator.CreateInstance(srcProp.GetType());

  srcProp.SetValue(newGameData, newOne, null);

And this is how to do it:

  var newOne = Activator.CreateInstance(srcProp.PropertyType);

  srcProp.SetValue(newGameData, newOne, null);
DShook
  • 14,833
  • 9
  • 45
  • 55
0

This is old thread. But solutions in this page didn't work. I did some adjust and works great (in .netcore 2.0)!

obj = Activator.CreateInstance<T>();
foreach (PropertyInfo prop in obj.GetType().GetProperties())
{
    if (!object.Equals(this.reader[prop.Name], DBNull.Value))
    {
        if (prop.PropertyType.Name.Contains("Nullable"))
        {
            prop.SetValue(obj, Convert.ChangeType(this.reader[prop.Name], Nullable.GetUnderlyingType(prop.PropertyType)), null);
        }
        else
        {
            prop.SetValue(obj, this.reader[prop.Name], null);
        }
    }
}
Fer R
  • 141
  • 2
  • 9
0

var properties = propTypes.GetProperty(attribute);

TypeCode typeCode = Type.GetTypeCode(properties.PropertyType);

switch (typeCode)
{
  case TypeCode.Int32:
  properties.SetValue(m, Convert.ToInt32(value.AsPrimitive().Value));
  break;
  case TypeCode.Int64:
  properties.SetValue(m, Convert.ToInt64(value.AsPrimitive().Value));
  break;
}
Sher Singh
  • 497
  • 5
  • 8
0

You may try something like this that worked for me for conversion to double?:

Type propType = propInfo.PropertyType;

if (propType.AssemblyQualifiedName.StartsWith("System.Nullable`1[[System.Double"))
{
    if (dataRow[column.ColumnName] == DBNull.Value)
    {
        propInfo.SetValue(obj, null, null);
    }
    else
        propInfo.SetValue(obj, (double?)dataRow[column.ColumnName], null);
}
else
    propInfo.SetValue(obj, dataRow[column.ColumnName], null);
Ashok Garg
  • 73
  • 1
  • 4