如何将一种对象类型的对象深度复制到另一种共享继承结构的对象类型

时间:2011-11-30 23:53:30

标签: c# inheritance clone

我目前正在为HL7消息构建对象模型。没有深入研究,我们的基本结构看起来与此相似:

  • 基础对象
    • 中介对象
      • DeepMessage1
      • DeepMessage2
    • 消息1
    • 消息2

我希望有一个深层复制/克隆,它将所有类似的属性从DeepMessage1复制到DeepMessage2或Message1或Message2。

public class BaseObject
{
    PersonName Name; //A personName includes a first name and last name.
}

public class IntermediaryObject : BaseObject
{
    Accident accident; //An accident codes and a description.
}

public class Message1 : BaseObject
{
    //this class doesn't actually contain any special handling, just used for 
    //easy understanding for the API
}

public class Message2 : BaseObject
{
    DateTime AdmissionDate; //Note the admission date is also contained in
                            //DeepMessage1, but not DeepMessage2
}

public class DeepMessage1 : IntermediaryObject
{
    DateTime AdmissionDate; //Note this property is also contained in Message2 and 
                            //needs to be copied
}

public class DeepMessage2 : IntermediaryObject
{
    DateTime DischargeDate;
}

考虑到这种结构,我希望能够创建一个对象与另一个对象共享的每个属性的深层副本。 This other question是一个非常好的开始,但最终我无法使用反射,因为这是浅层克隆,序列化需要完全相同的对象。

我最终得到了这段代码,但它只执行浅拷贝。

public T Copy<T>() where T : new()
{
    T destination = new T();
    if (destination is HL7Message)
    {
        foreach (var destinationProperty in destination.GetType().GetProperties())
        {
            foreach (var sourceProperty in this.GetType().GetProperties())
            {
                if (destinationProperty.Name.Equals(sourceProperty.Name))
                {
                    destinationProperty.SetValue(destination, destinationProperty.GetValue(this, null), null);
                }
            }
        }
        return destination;
    }
    else
    {
        throw new InvalidOperationException("The destination copy type is not an HL7Message object");
    }
}

我希望在我的if (destinationProperty.Name.Equals(sourceProperty.Name))块中,我可以尝试在任何具有特定基类型的属性(我库中的所有对象都扩展)上调用Copy。但是,由于无法在运行时确定T,我无法在该部分中使用Copy。

我是否只需要为特定对象设置单独的Copy类型,并让Messages使用Copy或者有没有办法做到这一点真是太疯狂了?

2 个答案:

答案 0 :(得分:3)

这是一个用于在对象之间复制属性的完整工具,请看一下:

https://github.com/AutoMapper/AutoMapper/wiki

答案 1 :(得分:1)

为了解决这个问题,我最终采用了两层方法。我并没有断言devman提到的地图无法奏效,但是由于时间有限,我无法探索他的解决方案。

首先,我的所有对象都扩展了相同的基础对象(示例中为BaseLibraryClass)。我最初这样做是因为每个类都是原始继承INotifyPropertyChanged。为了处理提升属性的变化,我创建了一个基础对象,继承了我的所有对象。在这个基类上,我还添加了一个抽象的DeepCopy()方法。每个对象都会覆盖DeepCopy()方法,方法是创建自身的新实例并将值赋值,或者在自定义属性的情况下,在这些属性上调用DeepCopy()。

Class Cat : BaseLibraryClass
{
    public string Name;
    public Collar MyCollar;

    public override object DeepCopy()
    {
        Cat destination = new Cat();
        Cat.Name = Name;
        Cat.MyCollar = MyCollar.DeepCopy();
    }
}

Class Collar { ... }

现在我在原始问题中将它与反射循环的修改版本结合起来。我希望能够执行此类复制的所有对象都继承了相同的基类。在示例中,是一个BaseCopyObject。

public T CopyObject<T>() where T : new()
{
    T destination = new T();
    if (destination is BaseCopyObject)
    {
        foreach (var destinationProperty in destination.GetType().GetProperties()) //iterate through the properties of the destination object.
        {
            foreach (var sourceProperty in this.GetType().GetProperties()) // iterate through the properties of the source object to determine if the property names match. If they do, copy the value.
            {
                //If we have one of our BaseCopyObjects, then set the value with the DeepCopy() method of that object.
                if (destinationProperty.Name.Equals(sourceProperty.Name))
                {
                    if (typeof(BaseLibraryClass).IsAssignableFrom(destinationProperty.PropertyType))
                    {
                        BaseCopyObject var = (BaseLibraryClass)sourceProperty.GetValue(this, null);
                        if (var != null)
                        {
                            destinationProperty.SetValue(destination, var.DeepCopy(), null);
                        }
                        break;
                    }
                    //If we have a list, get the list and iterate through the list objects.
                    else if (typeof(IList).IsAssignableFrom(destinationProperty.PropertyType))
                    {
                        IList destinationList = (IList)destinationProperty.GetValue(destination, null);
                        destinationList.Clear();
                        IList sourcelist = (IList)destinationProperty.GetValue(this, null);
                        if (sourcelist != null)
                        {
                            foreach (object listItem in sourcelist)
                            {
                                if (listItem is Base)
                                {
                                    BaseLibraryClass n = (BaseLibraryClass)listItem;
                                    destinationList.Add(n);
                                }
                            }
                        }
                        break;
                    }
                    //Finally we get to normal properties. Set those.
                    else
                    {
                        destinationProperty.SetValue(destination, destinationProperty.GetValue(this, null), null);
                        break;
                    }

                }
            }
        }
        return destination;
    }
    else
    {
        throw new InvalidOperationException("The destination copy type is not an BaseLibraryClass object");
    }
}