Основываясь на последующих ответах в комментариях, лучшее решение для того, что вы ищете, является самым простым: напишите код, чтобы скопировать объект самостоятельно.
Если ваш класс действительно такой же простой, как обертка вокруг словаря, в котором хранятся пары ключ / значение для пользовательских свойств, вы можете использовать конструктор 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
рефлексивно, но семантика того, как он вызывается, будет полностью неизвестна. Лучшее, что вы могли бы сделать, - это сохранить метаданные (с помощью пользовательского атрибута в классе) о том, какой конструктор вызывать и как его вызывать (например, список полей в порядке их передачи), но это требует предварительного знания механизма клонирования, а также подразумевает, что аргументы для конструктора являются полями исходного объекта, что не обязательно имеет место.
Теперь добавим еще одну загвоздку: неизменные ссылочные типы. Это также может привести к неожиданному поведению. Неизменяемые ссылочные типы - это ссылочные типы, чье (видимое снаружи) значение не может быть изменено; в большинстве случаев эти классы предназначены для демонстрации семантики типа значения. Им также часто не хватает конструкторов без параметров (и они могут вообще не иметь общедоступных конструкторов), что заставляет их страдать от нашего предыдущего раздражения, но они также могут использовать фабричный метод, чтобы гарантировать, что ссылочное равенство также означает значение равенство и наоборот (это последнее условие встречается реже, но если мы говорим об алгоритме, который является абсолютно наивным механизмом клонирования, то мы должны его охватить). Это, опять же, означает другой пользовательский атрибут, указывающий, что механизм клонирования должен просто копировать ссылку, а не клонировать фактический объект.
Итак, короче говоря, совершенно наивный механизм глубокого копирования просто невозможен при работе с произвольными типами. Типы должны быть разработаны с учетом механизма клонирования, и, возможно, придется идти на уступки или украшать себя определенным образом, чтобы работать с ним. Это, в сочетании с относительно нечастым требованием, вероятно, объясняет, почему сейчас нет механизма глубокого копирования на уровне платформы, и почему вы должны рассмотреть более явный механизм копирования, когда вы знаете , что копировать (а что нет важно), чтобы вы могли быть уверены, что то, что вы получаете, то, что вы хотите.