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