关于c#:复杂第三方对象/类的深层副本

Deep Copy of Complex Third Party Objects/Classes

本问题已经有最佳答案,请猛点这里访问。

我一直在做一个项目,用pdfview4net创建PDF表单。虽然库通常很好,但是表单创建者是原始的,在处理表单域(即文本框、复选框等)时缺少基本功能(如复制/粘贴、对齐、格式设置等)。

问题是:我一直在扩展字段对象的功能,并在复制/粘贴时被绊倒。要做到这一点,我需要一个对象的深度副本,而不引用任何原始内容。我给供应商发了电子邮件,要求他们提供有关复制这些对象的推荐方法的信息,他们回答说,我需要手动复制每个属性…头撞在桌子上。这些是大型类,具有多个嵌入类作为属性,以及UI元素。

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

我尝试/回顾的内容:我研究了各种方法来对一个物体进行深度复制,并逐一丢弃它们:

  • 手动地,通过刻苦的属性来实现属性:我尝试使用7个字段对象(pdfextboxfield)中的第一个,但是它很快就与同样是不同类型类的多个属性脱离了控制。最后,我仍然对原始对象有着挥之不去的引用,在那里创建了一个浅拷贝,而不是一个预期的深拷贝。
  • 序列化:类没有标记为可序列化,供应商也不会更改这一点。我让他们去,他们拒绝了。
  • iCloneable:需要由供应商实施。
  • automapper:这似乎是为了将数据从一个或多个对象类型复制到另一个对象类型。我使用的对象是同一类型的。不过,如果这是最好的解决方案,我并不反对使用它。
  • Emit Mapper:此项目似乎已被放弃。
  • MemberWiseClone:做一个浅拷贝,而不是我要找的深拷贝,尽管当发问者特别要求一个深拷贝时,在很多其他帖子上都建议这样做。
  • 值注入器:我在CodePlex上从ValueInjecter实现了FastDeepCloneInjection,但需要从中注入的大多数类没有0参数构造函数,这在为副本创建新实例时是必需的。ValueInjecter不允许跳过某些属性,或者我只跳过没有默认构造函数的项,并将其设置为空(默认)。我在第一节课上就遇到了这个问题。为了解决这个问题,我创建了一个继承自原始文件的包装器类,并将原始文件强制转换到包装器中(反之亦然),但我认为这不是一个好的解决方案。

编辑:我真的不觉得这个问题是重复的。我广泛搜索了一个解决方案,包括标记为副本/原件的邮件,但找不到令人满意的解决方案。如前所述,我无权更改需要复制的类。这会对DataContractSerializer、BinaryFormatter和任何其他类型的序列化进行折扣。这也会对我使用activator.createInstance看到的反射示例进行折扣,因为我需要复制的类中有95%没有接受0个参数的构造函数。这是我在使用ValueInjecter时遇到的相同问题。使用icloneable也会有折扣。


我会用automapper。考虑以下类定义:(注意private ctor)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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; }
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[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);
}