【问题标题】:Deep Copy of Complex Third Party Objects/Classes [duplicate]复杂第三方对象/类的深拷贝[重复]
【发布时间】:2015-04-28 01:49:51
【问题描述】:

我一直致力于使用 PDFView4Net 创建 PDF 表单的项目。虽然该库通常很好,但表单创建器是原始的,并且在使用表单字段(即文本框、复选框等)时缺乏基本功能(例如复制/粘贴、对齐、格式化等)。

问题:我一直在扩展字段对象的功能,但在复制/粘贴时遇到了问题。为此,我需要一个对象的深层副本,而不是对原始对象的任何引用。我给供应商发了电子邮件,询问他们推荐的复制这些对象的方法的信息,他们回复说我需要手动、手动地复制每个属性...在桌子上敲打头。这些是大型类,具有多个嵌入式类作为属性以及 UI 元素。

问题:是否有任何好的方法可以对不需要序列化、不需要访问或更改的复杂对象执行深层复制到源类并且不需要默认构造函数?

我尝试过/审查过的内容: 我研究了各种方法来制作一个对象的深拷贝,并一一丢弃:

  • 手动,Painstaking Property 的属性:我尝试使用 7 个字段对象中的第一个 (PDFTextBoxField),但它很快就失去了许多属性,这些属性也是不同类型的类。最后,我仍然对原始对象有挥之不去的引用,在该对象中创建了浅拷贝,而不是预期的深拷贝。
  • 序列化:类未标记为可序列化,供应商也不会更改这一点。我要求他们这样做,他们拒绝了。
  • ICloneable:需要由供应商实施。
  • AutoMapper:这似乎用于将数据从一种或多种对象类型复制到另一种对象类型。我正在使用的对象是同一类型。虽然如果它是最好的解决方案,我不反对使用它。
  • Emit Mapper:此项目似乎已被放弃。
  • MemberwiseClone:做浅拷贝,而不是我正在寻找的深拷贝,尽管当提问者特别要求深拷贝时,很多其他帖子都建议这样做。
  • Value Injecter:我在 CodePlex 上从 ValueInjecter 实现了 FastDeepCloneInjection,但大多数需要从中注入的类没有为副本创建新实例时需要的 0 参数构造函数。 ValueInjecter 不允许跳过某些属性,或者我会跳过没有默认构造函数的项目并将它们设置为 null(默认值)。我在第一堂课上就遇到了这个问题。为了试图解决这个问题,我创建了一个继承自原始的包装器类,并将原始包装器转换为包装器(返回时反之亦然),但我认为这不是一个好的解决方案。

编辑:我真的不觉得这个问题是重复的。我已经广泛搜索了解决方案,包括标记为重复/原始的帖子,但无法找到令人满意的解决方案。如前所述,我无权更改我需要复制的类。这会打折 DataContractSerializer、BinaryFormatter 和任何其他类型的序列化。这也降低了我使用 Activator.CreateInstance 看到的反射示例,因为我需要复制的大约 95% 的类没有采用 0 个参数的构造函数。这与我在使用 ValueInjecter 时遇到的问题相同。使用 ICloneable 也会打折。

【问题讨论】:

标签: c# clone automapper deep-copy valueinjecter


【解决方案1】:

我会为此使用 AutoMapper。考虑以下类定义:(注意私有 ctor)

public class Parent
{
    public string Field1 { get; set; }
    public Level1 Level1 { get; set; }
    public static Parent GetInstance()
    {
        return new Parent() { Field1 = "1", Level1 = new Level1 { Field2 = "2", Level2 = new Level2() { Field3 = "3"}}};
    }
    private Parent()  {              }
}

public class Level1
{
    public string Field2 { get; set; }
    public Level2 Level2 { get; set; }
}

public class Level2
{
    public string Field3 { get; set; }
}

然后您可以根据需要将 AutoMapper 设置为深度克隆:

[TestMethod]
public void DeepCloneParent()
{
    Mapper.CreateMap<Parent, Parent>();
    Mapper.CreateMap<Level1, Level1>();
    Mapper.CreateMap<Level2, Level2>();
    var parent = Parent.GetInstance();

    var copy = Mapper.Map<Parent, Parent>(parent);

    Assert.IsFalse(copy == parent);//diff object
    Assert.IsFalse(copy.Level1 == parent.Level1);//diff object
    Assert.IsFalse(copy.Level1.Level2 == parent.Level1.Level2);//diff object
    Assert.AreEqual("1", copy.Field1);
    Assert.AreEqual("2", copy.Level1.Field2);
    Assert.AreEqual("3", copy.Level1.Level2.Field3);
}

【讨论】:

  • 感谢您的推荐。我终于能够使用 AutoMapper 成功复制 7 个字段中的第一个。我仍然不确定这是最好的解决方案。 PDFView4Net 类库包含 266 个类。只需要 400 多行代码才能正确复制第一个代码。不过,我想这比手动完成要好。
  • @mslissap 为什么这么多行?? (第一个副本涉及多少类?)
  • 第一份副本有 116 个班级。供应商使用了很多“迷你”类来继承(属性是 C,从 B 继承,从 A 继承,从基类继承)。一个属性可能需要 4-5 个类。这是我需要复制的 7 个对象中最简单的一个,尽管其他对象使用许多相同的“基”类来继承。对于其中许多人,我不得不为 CreateMap.ConstructUsing 创建一个 ResolutionContext,因为没有无参数构造函数(我默认这些),然后创建一个 AfterMap 操作以从源中获取真正的值并进入目标。
  • @mslissap 哇,讨厌!但是,我不相信您要实现的目标有简单/快速的解决方案。从概念上讲(对经理说!)这似乎很容易,因为您只想复制一个对象,但现实要困难得多。至少你有能力编写好的测试来检查你的复制是否正确。
猜你喜欢
  • 2013-06-06
  • 2011-02-09
  • 2022-01-25
  • 2017-10-14
  • 1970-01-01
  • 2020-03-25
  • 2016-05-07
  • 2013-03-12
  • 2010-10-22
相关资源
最近更新 更多