除了 System.Type 之外,无需任何其他信息即可转换对象

发布于 2024-10-16 08:30:33 字数 2172 浏览 4 评论 0原文

对于 SO 问题,我最近编写了一个通用扩展方法,该方法应该加载从另一个对象,即将源的所有属性分配给目标,如果该属性是引用类型,则递归地执行此操作。我使用反射已经取得了很大的进展,但是当涉及到引用类型的属性类型时,我遇到了问题,这是我的第一种方法:

第一种方法:

    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 也可以轻松返回一个该死的对象,而且我似乎无法以其他方式将对象转换为它本来的样子。当然,我已经在网上寻找过这个问题的答案,但到目前为止我还没有成功。

For a SO question i quite recently wrote a generic extension method that should load an object from another, i.e. assign all the properties of the source to the target and do so recursively if the property is a reference-type. I got quite far using reflection but i hit a problem when it came to the types of properties that are reference-types, here is my first approach:

First approach:

    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);
                }
            }
        }
    }

The problem here is that PropertyInfo.GetValue returns an object, subsequently T will equal object in the recursive call and i can no longer get the properties that the object actually has.

I conceived of a workaround which requires you to pass the Type explicitly, which is quite redundant since in theory it should be possible to manage without:

    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);
                }
            }
        }
    }

I also tried using dynamic targetPropertyReference but then i get a runtime exception that the Load method cannot be found, it is infuriating.

Other than that Convert.ChangeType handily returns a bloody object too and i cannot seem to otherwise cast the object to what it is. Of course i have looked for an answer to this on the net but i have been unsuccessful so far.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

长伴 2024-10-23 08:30:33

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

选项 1:回避问题

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

将:替换

typeof(T).GetProperties()

为:

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

这当然会在代码中引入一个微小语义更改,因为它可以防止人们想要传递长颈鹿但只想要的情况Animal 属性已复制。如果 sourcetarget 具有不同的运行时类型(具有不同的属性),它也会崩溃,但是您可以解决这个问题,而不需要太多麻烦(例如找到最深的公共基类型并使用属性来代替)。


选项 2:更多反射/动态

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

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

顺便说一句,没有理由不能使用dynamic来完成非常相似的事情。我怀疑您正在尝试将调用“作为”扩展方法, 但这行不通。只需尝试正常的“静态方法”语法即可。

I haven't looked at the full code or considered the full consequences of implementing such a scheme (EDIT: what will happen if there are cyclic references?), but I can give you two short fixes for your specific problem:

Option 1: Dodge the issue

Use the run-time type of the provided "normal" argument, rather than the type-argument.

Replace:

typeof(T).GetProperties()

with:

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

This of course introduces a minor semantic change in your code, in that it prevents scenarios when one would would want to pass a Giraffe but only want Animal properties copied over. It will also blow up if source and target are of different run-time types (having different properties), but you can work around that without too much trouble (e.g. find the deepest common base-type and use its properties instead).


Option 2: More reflection / dynamic

The other solution of course, is to use MakeGenericMethod to allow the type-argument to be provided "dynamically". This maintains the original semantics of the code (untested):

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

Btw, there's no reason you can't use dynamic to accomplish something very similar. I suspect you're trying to do the call "as" an extension-method, but that won't work. Just try the normal "static-method" syntax.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文