对于SO question我最近编写了一个通用扩展方法,该方法应该从另一个加载一个对象,即将源的所有属性分配给目标,如果属性是引用类型,则递归地执行此操作。我使用反射得到了很多,但是当涉及到引用类型的属性类型时遇到了问题,这是我的第一个方法:
第一种方法:
public static void Load<T>(this T target, T source, bool deep)
{
foreach (PropertyInfo property in typeof(T).GetProperties())
{
if (property.CanWrite && property.CanRead)
{
if (!deep || property.PropertyType.IsPrimitive || property.PropertyType == typeof(String))
{
property.SetValue(target, property.GetValue(source, null), null);
}
else
{
property.GetValue(target, null).Load(property.GetValue(source, null), deep);
}
}
}
}
这里的问题是PropertyInfo.GetValue
返回一个对象,随后T
将在递归调用中等于object
,我再也无法获得该对象实际拥有的属性。
我设想了一种解决方法,它要求你明确地传递Type,这是非常多余的,因为理论上应该可以在没有的情况下进行管理:
public static void Load<T>(this T target, Type type, T source, bool deep)
{
foreach (PropertyInfo property in type.GetProperties())
{
if (property.CanWrite && property.CanRead)
{
if (!deep || property.PropertyType.IsPrimitive || property.PropertyType == typeof(String))
{
property.SetValue(target, property.GetValue(source, null), null);
}
else
{
object targetPropertyReference = property.GetValue(target, null);
targetPropertyReference.Load(targetPropertyReference.GetType(), property.GetValue(source, null), deep);
}
}
}
}
我也尝试使用dynamic targetPropertyReference
,但后来我得到了一个运行时异常,即无法找到Load
方法,这是令人愤怒的。
除了Convert.ChangeType
之外,还会轻易地返回一个血腥的object
,而我似乎无法将该对象强制转换为它。当然,我已经在网上找到了答案,但到目前为止我还没有成功。
答案 0 :(得分:2)
我没有查看完整代码或考虑实现此类方案的全部后果(编辑:如果存在循环引用会发生什么?),但我可以为您的具体提供两个简短修复问题:
选项1:避开问题
使用提供的“normal”参数的运行时类型,而不是 type-argument 。
替换:
typeof(T).GetProperties()
with:
// You need null-checks around this:
// you can't realistically continue if target is null anyway.
target.GetType().GetProperties()
这当然会在您的代码中引入次要语义更改,因为它可以防止人们希望传递Giraffe
但只需要复制Animal
属性的情况过度。如果source
和target
具有不同的运行时类型(具有不同的属性),它也会爆炸,但是你可以毫不费力地解决这个问题(例如找到最深的共同基类型和改为使用其属性。)
选项2:更多反思/动态
另一种解决方案当然是使用MakeGenericMethod
来允许“动态”提供type-argument。这维护了代码的原始语义(未经测试):
typeof(MyClass).GetMethod("Load")
.MakeGenericMethod(property.PropertyType)
.Invoke(null, property.GetValue(target, null),
property.GetValue(source, null), deep);
顺便说一句,没有的原因是你无法使用dynamic
来完成非常相似的事情。我怀疑你正试图将“呼叫”称为扩展方法,but that won't work。只需尝试正常的“静态方法”语法。