Как я могу глубоко скопировать объект одного типа объекта в другой тип объекта, которые разделяют структуру наследования - PullRequest
2 голосов
/ 01 декабря 2011

В настоящее время я создаю объектную модель для сообщений HL7. Не вдаваясь в них, базовая структура, которая у нас есть, выглядит примерно так:

  • Базовый объект
    • Посредник Объект
      • DeepMessage1
      • DeepMessage2
    • Сообщение1
    • message2

Я хочу иметь глубокую копию / клон, который будет копировать все подобные свойства из 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;
}

Учитывая эту структуру, я хочу иметь возможность создавать полную копию каждого свойства, которое один объект разделяет с другим. Этот другой вопрос был действительно хорошим началом, но в конечном итоге я не смог использовать отражение, так как это было мелкое клонирование, а для сериализации требуется точно такой же объект.

Я получил этот код, но он выполняет только поверхностную копию.

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 во время выполнения.

Мне просто нужно иметь отдельный тип Копирования для конкретных объектов, и чтобы Сообщения использовали Копию, или есть способ сделать это, что чертовски безумно?

Ответы [ 2 ]

3 голосов
/ 01 декабря 2011

Это полный инструмент для копирования свойств между объектами, посмотрите:

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

1 голос
/ 05 января 2012

Чтобы решить эту проблему, я остановился на двухуровневом подходе. Я не утверждаю, что карта, упомянутая Девманом, не могла бы сработать, но в течение ограниченного времени я не смог изучить его решение.

Во-первых, все мои объекты расширяли один и тот же базовый объект (например, 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");
    }
}
...