通过反射设置属性时键入转换问题

时间:2012-11-07 12:52:17

标签: c# reflection

我们有long?类型的属性,其中填充了int

当我直接设置属性obj.Value = v;时,这很好用但是当我尝试通过反射info.SetValue(obj, v, null);设置属性时,它给出了以下异常:

  

'System.Int32'类型的对象无法转换为'System.Nullable`1 [System.Int64]'类型。

这是一个简化的场景:

    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);
    }

为什么在直接设置属性时通过reflection设置属性时它不起作用?

7 个答案:

答案 0 :(得分:62)

查看完整文章:How to set value of a property using Reflection?

如果您为可空类型设置值

,则为完整代码
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<>));
}

您需要转换此类值,即您需要将值转换为您的属性类型,如下所示

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);

你需要这样做,因为你不能将任何arbirtary值转换为给定的类型......所以你需要像这样转换它

答案 1 :(得分:3)

当你写:

obj.Value = v;

编译器知道如何为您进行正确的转换并实际编译

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

当您使用反射时,没有编译器可以帮助您。

答案 2 :(得分:2)

因为类型long具有隐式转换方法。

6.1.2 Implicit numeric conversions

您可以将隐式转换方法视为=符号后面隐藏的方法。

它也适用于可空类型:

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

但反过来说不起作用,因为没有隐式转换来将null传递给非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.

您无需将int明确转换为int? ...或long?

但是,当您使用反射时,您将绕过隐式转换并将值直接分配给属性。这样,你必须明确地转换它。

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

反思会跳过隐藏在=后面的所有甜蜜的东西。

答案 3 :(得分:0)

根据Pranay Rana的建议(并SilverNinja回答)整合并扩展thecoop处理enum的答案,并将学习内容集中在一个地方。这是一个更复制/可用的。;

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<>));
}

答案 4 :(得分:0)

反射性地创建新对象时,我也遇到了这个问题。

这是不这样做的方法:

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

  srcProp.SetValue(newGameData, newOne, null);

这是方法:

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

  srcProp.SetValue(newGameData, newOne, null);

答案 5 :(得分:0)

这是旧线程。但是此页面中的解决方案不起作用。我做了一些调整,效果很好(在.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);
        }
    }
}

答案 6 :(得分:0)

var 属性 = 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;
}