Структурная или глубокая копия - C # - PullRequest
6 голосов
/ 09 июня 2011

Мне наконец-то удалось скопировать тип значения моего объекта, класса, который использует словарь для хранения динамических свойств.Мне было интересно узнать о двух вещах: моно-совместимости и эффективности.Я новичок в C #, и у меня еще есть чему поучиться программированию в целом, поэтому извиняюсь за неправильное использование нескольких фраз: P

Я использовал этот метод Как сделать глубокую копию объектав .NET (C # конкретно)? ... чтобы скопировать мой объект.У меня также будут сотни таких объектов, и было ли интересно, что их копирование таким образом было очень неэффективным?Структуры были бы лучшим вариантом?Однако я не уверен, когда использовать структуры.А это может быть перенесено с моно?Некоторые люди считают, что эта сериализация может вызвать проблемы.

Ответы [ 2 ]

3 голосов
/ 10 июня 2011

Основываясь на последующих ответах в комментариях, лучшее решение для того, что вы ищете, является самым простым: напишите код, чтобы скопировать объект самостоятельно.

Если ваш класс действительно такой же простой, как обертка вокруг словаря, в котором хранятся пары ключ / значение для пользовательских свойств, вы можете использовать конструктор Dictionary (IDictionary) , чтобы копировать значения из одного словаря в другой .

class MyWrapper
{
    private Dictionary<string, object> properties = 
                                         new Dictionary<string, object>();

    public MyWrapper Clone()
    {
        MyWrapper output = new MyWrapper();

        output.properties = new Dictionary<string, object>(properties);
    }
}

Очевидно, что это упрощенный класс, который на самом деле не делает ничего, но из того, что вы описали, вы получите то, что вам нужно. Никаких размышлений, никаких «ошибок», просто простая копия значений из одного словаря в другой.

EDIT

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

Концепция подлинной глубокой копии любого произвольного типа не так легко (или даже безопасно) реализовать в объектно-ориентированном языке, основанном на ссылках. В то время как простые классы были бы тривиальны для копирования, ссылки на циклы, классы без конструкторов без параметров и неизменяемые типы представляют проблемы.

Например, рассмотрим этот класс:

public class Foo
{
    public Foo Next { get; set; }
}

Что является в значительной степени самой простой реализацией односвязного списка. Наивный алгоритм глубокого копирования начинался бы с первого экземпляра Foo, а затем клонировал его, рекурсивно перемещаясь по цепочке Next ссылок, пока не встретил значение null. Однако, сделав это, мы не только израсходуем память, но и получим объекты, которые не представляют фактический клон оригинала:

Foo first = new Foo();

first.Next = new Foo();

first.Next.Next = first;

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

Dictionary<object, object> clonedObjects;

Теперь в алгоритме клонирования при присваивании значений свойствам или полям мы проверяем кэш, чтобы убедиться, что ссылка, которую мы собираемся дублировать, уже была клонирована. Если это так, мы используем это значение вместо клонирования нового. Это даст нам совершенно новый объектный граф, который представляет наш оригинальный объект, а также является полным клоном. Отлично, правда?

А как насчет конструкторов без параметров? Этот даже не решаем в совершенно общем смысле. Если я создам этот класс:

public class Bar
{
    public Bar(string gotcha) { }
}

Нет способа клонировать этот класс в наивном смысле; поскольку у вас нет способа узнать , как вызвать конструктор (вы можете получить ConstructorInfo рефлексивно, но семантика того, как он вызывается, будет полностью неизвестна. Лучшее, что вы могли бы сделать, - это сохранить метаданные (с помощью пользовательского атрибута в классе) о том, какой конструктор вызывать и как его вызывать (например, список полей в порядке их передачи), но это требует предварительного знания механизма клонирования, а также подразумевает, что аргументы для конструктора являются полями исходного объекта, что не обязательно имеет место.

Теперь добавим еще одну загвоздку: неизменные ссылочные типы. Это также может привести к неожиданному поведению. Неизменяемые ссылочные типы - это ссылочные типы, чье (видимое снаружи) значение не может быть изменено; в большинстве случаев эти классы предназначены для демонстрации семантики типа значения. Им также часто не хватает конструкторов без параметров (и они могут вообще не иметь общедоступных конструкторов), что заставляет их страдать от нашего предыдущего раздражения, но они также могут использовать фабричный метод, чтобы гарантировать, что ссылочное равенство также означает значение равенство и наоборот (это последнее условие встречается реже, но если мы говорим об алгоритме, который является абсолютно наивным механизмом клонирования, то мы должны его охватить). Это, опять же, означает другой пользовательский атрибут, указывающий, что механизм клонирования должен просто копировать ссылку, а не клонировать фактический объект.

Итак, короче говоря, совершенно наивный механизм глубокого копирования просто невозможен при работе с произвольными типами. Типы должны быть разработаны с учетом механизма клонирования, и, возможно, придется идти на уступки или украшать себя определенным образом, чтобы работать с ним. Это, в сочетании с относительно нечастым требованием, вероятно, объясняет, почему сейчас нет механизма глубокого копирования на уровне платформы, и почему вы должны рассмотреть более явный механизм копирования, когда вы знаете , что копировать (а что нет важно), чтобы вы могли быть уверены, что то, что вы получаете, то, что вы хотите.

1 голос
/ 09 июня 2011

Структуры имеют некоторые уникальные характеристики, которые следует тщательно рассмотреть перед их использованием.

  • Все они являются типами значений (это будет добавить проблемы для прохождения их вокруг)
  • Все они занимают память в стеке. вместо кучи
  • они должны иметь параметры без параметров Конструкторы
  • нет наследования для структур

http://msdn.microsoft.com/en-us/library/aa288471(v=vs.71).aspx

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

Обычно я бы придерживался только написания метода для моего класса, который создает глубокую копию. Вероятно, вы могли бы получить реальную фантазию и использовать комбинацию обобщений и рефлексии, чтобы написать довольно абстрактный DeepClone<T>(T object) метод. Или идите по легкой дороге и просто напишите одного портного, подходящего для данного класса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...