4

Essentially, I'm trying to use

field.SetValue(obj, val);

Where val's type can be implicitly converted to the true field type, but isn't 'directly' assignable. Of course, I get the usual ArgumentException: Object type cannot be converted to target type. Is there a way to do this without manually finding and calling the constructor?

John Saunders
  • 160,644
  • 26
  • 247
  • 397

2 Answers2

2

Try with Convert.ChangeType:

field.SetValue(obj, Convert.ChangeType(val, field.PropertyType), null);

Check the following Setting a property by reflection with a string value for further info.

Community
  • 1
  • 1
fcuesta
  • 4,429
  • 1
  • 18
  • 13
  • I tried that, however I'd then need to implement IConvertible interfaces, which I could do, but I'm trying to find another way –  Aug 22 '13 at 00:35
2

This is a quite complicated question, and I don't know a way to make it work in a sigle line. While the Convert.ChangeType works for simple cases, it will fail in the following cases :

  • The target type is a Nullable enum, and you use an integer value (not an enum value) : in that case, you'l need use Enum.ToObject to make it work.

  • Your value is DBNull.Value : You will need to test it and assign null in that case

  • The target type is not the same type of number as the value you want to set : Convert.ChangeType will help you here.

Here's a sample that illustrates how to do it :

public void SetFieldValue(FieldInfo field, object targetObj, object value)
{
    object valueToSet;

    if (value == null  || value == DBNull.Value)
    {
      valueToSet = null;
    }
    else
    {
      Type fieldType = field.FieldType;
      //assign enum
      if (fieldType.IsEnum)
          valueToSet = Enum.ToObject(fieldType, value);
      //support for nullable enum types
      else if (fieldType.IsValueType && IsNullableType(fieldType))
      {
          Type underlyingType = Nullable.GetUnderlyingType(fieldType);
          valueToSet = underlyingType.IsEnum ? Enum.ToObject(underlyingType, value) : value;
      }
      else
      {
          //we always need ChangeType, it will convert the value to the proper number type, for example.
          valueToSet = Convert.ChangeType(value, fieldType);
      }
    }
    field.SetValue(targetObj, valueToSet);
}

A unit test for the function :

enum TestEnum
{
    DummyValue
}

class TestClass
{
    public int IntValue;
    public decimal DecimalValue;
    public int? NullableInt;
    public TestEnum EnumValue;
    public TestEnum? NullableEnumValue;
    public TestClass ObjectValue;
}

[TestFixture]
public class DataObjectBinderFixture
{
    private TestClass _testObject;

    private void SetFieldValue(string fieldName, object value)
    {
        var fieldInfo = typeof (TestClass).GetField(fieldName);
        ReflectionUtils.SetFieldValue(fieldInfo, _testObject, value);
    }

    [Test]
    public void TestSetValue()
    {
        _testObject = new TestClass();

        SetFieldValue("IntValue", 2.19);
        SetFieldValue("IntValue", DBNull.Value);
        SetFieldValue("DecimalValue", 1);

        SetFieldValue("NullableInt", null);
        SetFieldValue("NullableInt", 12);

        SetFieldValue("EnumValue", TestEnum.DummyValue);
        SetFieldValue("EnumValue", 0);

        SetFieldValue("NullableEnumValue", TestEnum.DummyValue);
        SetFieldValue("NullableEnumValue", null);
        SetFieldValue("NullableEnumValue", 0);
        SetFieldValue("NullableEnumValue", DBNull.Value);

        SetFieldValue("ObjectValue", DBNull.Value);

    }
}
igelineau
  • 763
  • 9
  • 14