【问题标题】:Cast object without any more info than its System.Type没有任何比其 System.Type 更多信息的对象
【发布时间】:2023-03-11 11:08:02
【问题描述】:

对于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,我无法再获得该对象实际具有的属性。

我构想了一种解决方法,它要求您显式传递类型,这是非常多余的,因为理论上它应该可以在没有以下情况下进行管理:

    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,我似乎无法将对象投射到它的本来面目。当然我已经在网上寻找过这个问题的答案,但到目前为止我一直没有成功。

【问题讨论】:

    标签: c# generics reflection types extension-methods


    【解决方案1】:

    我没有查看完整的代码或考虑实施这种方案的全部后果(编辑:如果有循环引用会发生什么?),但我可以为您的 特定提供两个简短的修复 问题:

    选项 1:回避问题

    使用提供的“正常”参数的运行时类型,而不是类型参数

    替换:

    typeof(T).GetProperties()
    

    与:

    // You need null-checks around this:
    // you can't realistically continue if target is null anyway.
    target.GetType().GetProperties()
    

    这当然会在您的代码中引入 次要 语义更改,因为它可以防止人们想要传递 Giraffe 但只想复制 Animal 属性的情况。如果sourcetarget 是不同的运行时类型(具有不同的属性),它也会爆炸,但您可以轻松解决这个问题(例如,找到最深的通用基本类型并使用 属性)。


    选项 2:更多反射/动态

    当然,另一种解决方案是使用MakeGenericMethod 允许“动态”提供类型参数。这保持了代码的原始语义(未经测试):

    typeof(MyClass).GetMethod("Load")
                   .MakeGenericMethod(property.PropertyType)
                   .Invoke(null, property.GetValue(target, null), 
                                 property.GetValue(source, null), deep);
    

    顺便说一句,你不能使用dynamic 来完成非常相似的事情是有的。我怀疑您正在尝试将呼叫“作为”扩展方法but that won't work。只需尝试正常的“静态方法”语法。

    【讨论】:

    • 不可能!我以为我试过target.GetType(),非常感谢!我认为会这样做(我的意思是,如果某些东西被装箱,那么这样做的可能性有多大,只是为了更改所有超类型特定的属性,以便稍后将其拆箱)。不会将此标记为答案,因为它没有回答实际问题(或者是吗?),我会更改问题本身,但如果在一个月内没有答案出现。
    • @H.B.:如果您能告诉我这不能回答问题,那就太好了。我认为选项 2 在保持代码语义的同时可以满足您的要求。
    • 我不熟悉你在选项 2 中所做的事情,所以我会告诉你这些确实是我正在寻找的机器人 :)
    • @H.B.:这是我提出的一个真正的问题,而不是要求接受答案的方式。
    • @HB:本质上,选项 2 在运行时计算出 T 应该是什么,创建一个带有 that 类型参数的方法版本,然后调用它.
    猜你喜欢
    • 1970-01-01
    • 2019-07-10
    • 2014-02-19
    • 2011-10-11
    • 2023-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多