31

我们有一个类型的属性,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而直接设置属性时不起作用?

4

8 回答 8

77

查看全文: 如何使用反射设置属性的值?

如果您正在为可空类型设置值,则为完整代码

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

您需要这样做,因为您无法将任何任意值转换为给定类型...所以您需要像这样转换它

于 2012-11-07T12:59:39.367 回答
4

当你写:

obj.Value = v;

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

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

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

于 2012-11-07T13:01:06.960 回答
2

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

6.1.2 隐式数值转换

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

它也适用于可为空的类型:

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

但是反过来是行不通的,因为不存在将 a 传递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.

您不必将 a 显式转换intint?... 或 a long?

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

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

反射跳过所有隐藏在背后的甜蜜的东西=

于 2012-11-07T13:01:43.303 回答
2

按照 SilverNinja的建议(并由thecoop回答)巩固和扩展Pranay Rana对处理s 的回答,并将积累的知识集中在一个地方。这是一个多一点的复制/粘贴。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<>));
}
于 2018-03-20T16:39:51.693 回答
0

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

这是不这样做的方法:

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

  srcProp.SetValue(newGameData, newOne, null);

这是如何做到的:

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

  srcProp.SetValue(newGameData, newOne, null);
于 2018-10-17T01:06:41.690 回答
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);
        }
    }
}
于 2020-04-24T14:21:19.020 回答
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;
}
于 2021-02-15T13:46:56.807 回答
0

您可以尝试这样的方法,它对我转换为双倍有用吗?:

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);
于 2021-11-16T05:58:59.060 回答