用反射'铸造'

时间:2009-09-09 10:23:02

标签: c# reflection casting

请考虑以下示例代码:

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

现在我需要通过反思来做类似的事情:

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

请注意,我不能假设PropertyInfo总是表示long,而且该值始终不是小数。但是,我知道可以将值转换为该属性的正确类型。

如何通过反射将'value'参数转换为PropertyInfo实例表示的类型?

6 个答案:

答案 0 :(得分:125)

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

答案 1 :(得分:38)

Thomas的答案是正确的,但我想我会添加我的发现,即Convert.ChangeType不会处理转换为可空类型。为了处理可空类型,我使用了以下代码:

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

此代码使用以下扩展方法:

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

答案 2 :(得分:36)

托马斯回答仅适用于实现IConvertible接口的类型:

  

要使转换成功,value必须实现IConvertible接口,因为该方法只是将调用包装到适当的IConvertible方法。该方法要求支持将值转换为conversionType。

此代码编译一个linq表达式,用于执行拆箱(如果需要)和转换:

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

得到的lambda表达式等于(TOut)(TIn)数据,其中TIn是原始数据的类型,TOut是给定类型

答案 3 :(得分:10)

有助于jeroenh的回答,我想补充一点,Convert.ChangeType崩溃时为空值,因此获取转换值的行应为:

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

答案 4 :(得分:2)

当Type是Nullable Guid时,上述提议的解决方案都不起作用。 System.DBNull

引发了从“System.Guid”到“Convert.ChangeType”异常的无效广播

将此更改修复为:

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

答案 5 :(得分:0)

这是一个非常老的问题,但是我认为我很喜欢ASP.NET Core Googlers。

在ASP.NET Core中,.IsNullableType()受保护(以及其他更改),因此代码略有不同。这是@jeroenh的答案,修改后可以在ASP.NET Core中使用:

void SetValue(PropertyInfo info, object instance, object value)
{
    Type proptype = info.PropertyType;
    if (proptype.IsGenericType && proptype.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
    {
        proptype = new NullableConverter(info.PropertyType).UnderlyingType;
    }

    var convertedValue = Convert.ChangeType(value, proptype);
    info.SetValue(instance, convertedValue);
}