Универсальный конструктор копирования C#

Как лучше всего написать универсальную функцию конструктора копирования для моих классов С#? Все они наследуются от абстрактного базового класса, поэтому я мог бы использовать отражение для сопоставления свойств, но мне интересно, есть ли лучший способ?


person lomaxx    schedule 11.01.2009    source источник


Ответы (5)


Вы можете эффективно создать поверхностную копию с отражением, предварительно скомпилировав ее, например, с помощью Expression. Например, вот так.

Для глубоких копий наиболее надежным подходом является сериализация.

person Marc Gravell    schedule 11.01.2009

Конструктор копирования в основном означает, что у вас есть один параметр, который является объектом, который вы собираетесь копировать.

Кроме того, делайте глубокую копию, а не поверхностную.

Если вы не знаете, что такое глубокие и мелкие копии, то вот что:

Предположим, вы копируете класс, который имеет одну строку целых чисел в качестве поля.

Неглубокая копия будет:

public class Myclass()
{
    private int[] row;
    public MyClass(MyClass class)
    {
        this.row = class.row
    }
}

глубокая копия:

public class Myclass()
{
    private int[] row;
    public MyClass(MyClass class)
    {
        for(int i = 0; i<class.row.Length;i++)
        {
            this.row[i] = class.row[i];
        }
    }
}

Глубокая копия действительно получает фактические значения и помещает их в новое поле нового объекта, в то время как неглубокая копия копирует только указатели.

С мелкой копией, если вы установите:

row[3] = 5;

И затем распечатайте обе строки, оба отпечатка будут иметь 5 как значение 4-го числа. Однако при глубоком копировании это будет только первый отпечаток, поскольку строки не имеют одинаковых указателей.

person Vordreller    schedule 11.01.2009
comment
Вы можете использовать row.clone(), так как С# позволяет клонировать примитивные массивы. - person Kristopher Ives; 15.04.2014
comment
Разве приведенный выше код не дает сбой из-за того, что row никогда не инициализируется? - person AnotherParker; 03.06.2014

Избегайте отражения, если можете. Каждый класс должен нести ответственность за копирование собственных свойств и отправку их дальше базовому методу.

person Øyvind Skaar    schedule 11.01.2009
comment
поэтому просто для уточнения, было бы лучше, если бы у вас был собственный конструктор копирования для каждого класса, который явно отображал бы свойства? - person lomaxx; 12.01.2009
comment
Я буду. Насколько сложно писать? Конечно, легче читать с первого взгляда, чем метод, основанный на отражении. - person duffymo; 12.01.2009
comment
Да, я бы рекомендовал такой подход. - person Øyvind Skaar; 12.01.2009
comment
Его нетрудно прочитать и, безусловно, легко написать, однако необходимо учитывать проблемы с обслуживанием. Если вы добавляете новое свойство в класс, вам нужно обновить конструктор копирования и тесты, о которых довольно легко забыть. - person lomaxx; 12.01.2009
comment
Я бы не согласился с обновлением конструктора копирования. И в зависимости от того, что изменилось в новом свойстве, я не уверен, что вам нужно будет делать что-то большее, чем просто повторно запустить существующие тесты. Добавление еще одной пары get/set не требует новых модульных тестов для каждого. - person duffymo; 12.01.2009
comment
как бы вы скопировали недавно добавленные свойства, если вы не обновляете конструктор копирования? - person lomaxx; 12.01.2009

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

public partial class LocationView : Location
{
    public LocationView() {}

    // base class copy constructor
    public LocationView(Location value) {
        Type t = typeof(Location);
        PropertyInfo[] properties = t.GetProperties();
        foreach (PropertyInfo pi in properties)
        {
            pi.SetValue(this, pi.GetValue(value, null), null);
        }
    }
    public Quote Quote { get; set; }
}
person B2K    schedule 01.05.2012
comment
Одна строка: foreach (var propertyInfo in typeof(LocationView).BaseType.GetProperties()) propertyInfo.SetValue(this, propertyInfo.GetValue(obj_to_copy_parameter, null), null); вы также можете использовать инициализатор поля, чтобы читать GetProperties только один раз. - person Chris Marisic; 16.12.2014
comment
@ChrisMarisic Я стараюсь избегать однострочников, когда они жертвуют удобочитаемостью. - person B2K; 17.12.2014
comment
См. также stackoverflow. com/questions/14218989/ Я придумал общее расширение для общих свойств неглубокого клонирования - person B2K; 17.12.2014

Вы можете ссылаться на пакеты nuget valueinjecter и fastflect и использовать:

public class Myclass()
{
    private string _property;
    public MyClass(MyClass obj)
    {
         this.InjectFrom(obj.DeepClone());
    }
}
person Nikita Ilin    schedule 30.11.2017
comment
Почему нет плюсов? Может кто-нибудь подтвердить, работает ли это? - person James Hirschorn; 18.08.2020