复杂第三方对象/类的深层复制

时间:2015-02-25 21:11:43

标签: c# clone automapper deep-copy valueinjecter

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

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

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

我尝试/审核的内容 : 我已经研究了各种方法来制作一个对象的深层副本并逐个丢弃它们:

  • 手动,属性由艰苦属性:我尝试使用7个字段对象中的第一个(PDFTextBoxField),但它很快就失控了,这些属性也是不同类型的类。最后,我仍然对原始对象进行了延迟引用,其中创建了一个浅层副本,而不是按照预期创建了深层副本。
  • 序列化:类未标记为Serializable,供应商也不会更改此类。我问他们,他们说没有。
  • ICloneable:需要由供应商实施。
  • AutoMapper:这似乎是用于将数据从一个或多个对象类型复制到另一个对象类型。我正在使用的对象是相同的类型。如果它是最好的解决方案,我不反对使用它。
  • Emit Mapper:这个项目似乎已被放弃。
  • MemberwiseClone:是否有浅层副本,而不是我正在寻找的深层副本,尽管当提问者明确要求深层复制时,会在大量其他帖子中提出这一点。
  • Value Injecter:我在CodePlex上从ValueInjecter实现了FastDeepCloneInjection,但是大多数需要注入的类都没有0参数构造函数,这在为副本创建新实例时是必需的。 ValueInjecter不允许跳过某些属性,或者我只是跳过没有默认构造函数的项,并将它们设置为null(默认值)。第一堂课就是我立刻遇到的。为了解决这个问题,我创建了一个从原始文件继承的包装类,并将原始文件转换为包装器(反之亦然),但我不认为这是一个很好的解决方案。

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

1 个答案:

答案 0 :(得分:2)

我会使用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);
}