C# ';铸造';深思熟虑

C# ';铸造';深思熟虑,c#,reflection,casting,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)v

考虑以下示例代码:

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总是表示长值,也不能假设该值总是小数。但是,我知道可以将值转换为该属性的正确类型


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

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

void SetValue(PropertyInfo info, object instance, object value)
{
    info.SetValue(instance, Convert.ChangeType(value, info.PropertyType));
}
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<>));
    }
}
公共静态类类型扩展
{
公共静态bool IsNullableType(此类型)
{
返回类型。IsGenericType
&&type.GetGenericTypeDefinition().Equals(typeof(null));
}
}

对于jeroenh的答案,我想补充一点,Convert.ChangeType崩溃时会出现空值,因此获取转换值的行应该是:

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

如果类型为可为空的Guid,则上述建议的解决方案均无效。 从“
System.DBNull
”到“
System.Guid
”的无效强制转换在
Convert.ChangeType

要将该更改修复为:

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

Thomas answer仅适用于实现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是给定的类型。这是一个非常古老的问题,但我想我会加入到ASP.NET核心Google中

在ASP.NET核心中,
.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);
}
void SetValue(属性信息、对象实例、对象值)
{
类型proptype=info.PropertyType;
if(proptype.IsGenericType&&proptype.GetGenericTypeDefinition().Equals(typeof(null)))
{
proptype=新的NullableConverter(info.PropertyType).UnderlinedType;
}
var convertedValue=Convert.ChangeType(值,proptype);
info.SetValue(实例,convertedValue);
}

此问题并非特定于Guid,而是由于通过ADO.Net从数据库中获取空值时,您得到的是
DBNull.Value
,而不是简单的
null
。例如,对于nullable int,您将看到相同的结果。这实际上就是我要寻找的答案。非IConvertible动态强制转换。呵呵,如果我是OP,我会的。我希望在尝试将
IEnumerable
(其中这些对象是字符串)强制转换为
IEnumerable
时,这能救我一命。不幸的是,我遇到了类似
无法将'System.Collections.Generic.IEnumerable'1[System.object]'类型的对象强制转换为'System.Collections.Generic.IEnumerable'1[System.String]'类型的错误。
@derekantrican您需要迭代列表并自行强制转换每个对象。请注意
转换.ChangeType(value,property.PropertyType)
未实现
IConvertible
接口,则代码>仍可能失败。例如,如果
info.PropertyType
是某个
IEnumerable