C# 用反射“铸造”
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1398796/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
'casting' with reflection
提问by jeroenh
Consider the following sample code:
考虑以下示例代码:
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;
}
Now I need to do something similar through reflection:
现在我需要通过反射做类似的事情:
void SetValue(PropertyInfo info, object instance, object value)
{
// throws System.ArgumentException: Decimal can not be converted to Int64
info.SetValue(instance, value)
}
Note that I cannot assume that the PropertyInfo always represents a long, neither that value is always a decimal. However, I know that value can be casted to the correct type for that property.
请注意,我不能假设 PropertyInfo 始终表示 long,该值也不总是小数。但是,我知道可以将值转换为该属性的正确类型。
How can I convert the 'value' parameter to the type represented by PropertyInfo instance through reflection ?
如何通过反射将“值”参数转换为由 PropertyInfo 实例表示的类型?
采纳答案by Thomas Levesque
void SetValue(PropertyInfo info, object instance, object value)
{
info.SetValue(instance, Convert.ChangeType(value, info.PropertyType));
}
回答by jeroenh
The answer by Thomas is right, but I thought I would add my finding that Convert.ChangeType does not handle conversion to nullable types. To handle nullable types, I used the following code:
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);
}
This code makes use of the following extension method:
此代码使用以下扩展方法:
public static class TypeExtensions
{
public static bool IsNullableType(this Type type)
{
return type.IsGenericType
&& type.GetGenericTypeDefinition().Equals(typeof(Nullable<>));
}
回答by Ignacio Calvo
Contributing to jeroenh's answer, I would add that Convert.ChangeType crashes with a null value, so the line for getting the converted value should be:
为 jeroenh 的回答做出贡献,我会补充说 Convert.ChangeType 崩溃时为空值,因此获取转换值的行应该是:
var convertedValue = value == null ? null : Convert.ChangeType(value, targetType);
回答by Loukas
When the Type is a Nullable Guid then none of the above proposed solutions work.
Invalid cast from 'System.DBNull
' to 'System.Guid
' exception is thrown at Convert.ChangeType
当 Type 是 Nullable Guid 时,上述建议的解决方案都不起作用。从“ System.DBNull
”到“ System.Guid
”的无效转换抛出异常Convert.ChangeType
To fix that change to:
将该更改修复为:
var convertedValue = value == System.DBNull.Value ? null : Convert.ChangeType(value, targetType);
回答by Rafael
Thomas answer only works for types that implement IConvertible interface:
Thomas 的回答仅适用于实现 IConvertible 接口的类型:
For the conversion to succeed, value must implement the IConvertible interface, because the method simply wraps a call to an appropriate IConvertible method. The method requires that conversion of value to conversionType be supported.
要使转换成功,value 必须实现 IConvertible 接口,因为该方法只是包装对适当 IConvertible 方法的调用。该方法要求支持值到conversionType 的转换。
This code compile a linq expression that does the unboxing (if needed) and the conversion:
此代码编译一个 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;
}
The resulting lambda expression equals to (TOut)(TIn)Data where TIn is the type of the original data and TOut is the given type
生成的 lambda 表达式等于 (TOut)(TIn)Data,其中 TIn 是原始数据的类型,TOut 是给定的类型
回答by chakeda
This is a very old question but I thought I'd chime in for ASP.NET Core Googlers.
这是一个非常古老的问题,但我想我会为 ASP.NET Core Google 员工提供帮助。
In ASP.NET Core, .IsNullableType()
is protected (amongst other changes) so the code is a tad different. Here's @jeroenh's answer modified to work in ASP.NET Core:
在 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);
}