【问题标题】:Deep copies in 2021 (non-serializable objects) [duplicate]2021 年的深拷贝(不可序列化的对象)[重复]
【发布时间】:2021-08-20 02:47:54
【问题描述】:

我需要制作一个对象的深层副本,其中引用了引用另一层对象的其他对象,依此类推。

我没有直接访问该对象的权限,因此无法对其进行序列化。此外,我需要复制标记为私有、受保护或内部的字段(从包含对象的程序集以外的程序集)。

我已经阅读了很多很多关于如何在 C# 中进行深度复制的答案,它们都回到了序列化或实现 clone() 或类似的问题上。不幸的是,当这些方法不可用时,似乎很难找到关于如何进行深度复制的答案。

谁能告诉我如何在 C# 中对对象进行深度复制而无需访问/无法修改对象的源代码?这甚至可能吗?

提前谢谢你。

【问题讨论】:

  • 反射会让你这样做,但你需要一些界限。例如,如果您对带有循环的图中的第一个节点进行深层复制,会发生什么情况。是否要同时复制字段和读/写属性。如果您这样做,您可能想要构建一些复印机对象,然后将它们存储在字典中(使用 Type 作为索引)。这似乎是很多工作。我很好奇你为什么要这样做?
  • 我假设您已经看到了我选择为duplicate 的问题...您可能希望edit 这个问题列出所有 的建议它不适合您的简要原因。我不相信会有任何其他选项(因为这些选项应该已经添加到整体“深拷贝”选项中),但至少会更清楚您的实际限制是什么。 IE。 @Flydog57 建议的反射已包含在副本中,但此问题并未解释为什么它不适用于您的情况。
  • 等等。这就是问题所在。

标签: c# deep-copy


【解决方案1】:

某些类型的对象不适合被克隆,所以我们马上就知道永远不会有一种通用方法适用于所有情况。首先你需要知道类型可以被克隆。它需要有一个默认构造函数来做一件事。以及实际数据,而不是按需获取。

对于深度克隆,您会遇到额外的问题,这些问题在前面不一定会很明显。您如何处理具有不可克隆值的字段?您如何区分仅作为隐藏字段代理的属性与执行各种读取或写入操作的属性? event会员你们是怎么处理的?有许多常见结构会干扰您的克隆过程。

让我们暂时假设您要克隆的类型没有这些问题,图表中的任何对象也没有。您想创建一个可以处理基础的递归克隆方法。

在这种情况下,您只需要递归复制字段值,完全忽略属性。对于您复制的每个值 - 包括第一个 - 检查类型,如果它是您可以按值复制的东西(null 值,所有值类型,包括结构和一些不变类型,如 string),然后递归地克隆其余部分。

假设您没有遇到没有默认构造函数的对象,您可以使用Activator.CreateInstance() 来启动每个对象克隆。

您可以用大约 25 行代码完成一个简单的实现。这是一个糟糕的实现,仅用于演示目的,永远不要在生产中使用:

static T DeepClone<T>(T instance)
    where T : class, new()
{
    var visited = new Dictionary<object, object>();

    object DoClone(object inst)
    {
        if (inst is null)
            return null;
        if (visited.TryGetValue(inst, out var prev))
            return prev;
        
        var type = inst.GetType();
        var result = visited[inst] = Activator.CreateInstance(type);
        
        var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
        foreach (var field in fields)
        {
            var value = field.GetValue(inst);
            if (field.FieldType.IsValueType || field.FieldType == typeof(string))
                field.SetValue(result, value);
            else
                field.SetValue(result, DoClone(value));
        }
        return result;
    }

    return (T)DoClone(instance);
}

标准免责声明:它不漂亮,没有经过很好的测试,并且几乎可以保证在最不方便的时候破坏你。不处理任何类型的数组或集合。可能对儿童或小动物有害。醉酒时请勿操作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-06-06
    • 1970-01-01
    • 2015-09-14
    • 2014-10-10
    • 1970-01-01
    • 2011-02-09
    • 1970-01-01
    • 2019-12-05
    相关资源
    最近更新 更多