Я хочу сделать что-то вроде:
MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = myObj.Clone();
Затем внесите изменения в новый объект, которые не будут отражены в исходном объекте.
Мне часто не нужны эти функции, поэтому, когда это было необходимо, я прибегал к созданию нового объекта, а затем копировал каждое свойство в отдельности, но это всегда оставляло у меня ощущение, что есть лучший или более элегантный способ обработки ситуация.
Как я могу клонировать или глубоко копировать объект, чтобы клонированный объект мог быть изменен без каких-либо изменений, отраженных в исходном объекте?
clone
в классе создается метод, а затем вызывается внутренний, закрытый конструктор, который передаетсяthis
. Таким образом, копирование ужасно, но копирование тщательно (и статья определенно стоит прочитать) - нет. ; ^)Ответы:
В то время как стандартная практика заключается в реализации
ICloneable
интерфейса (описанного здесь , поэтому я не буду отрыгивать), вот хороший копир с глубоким клоном объектов, который я нашел в The Code Project некоторое время назад и включил в наши материалы.Как упоминалось в другом месте, он требует, чтобы ваши объекты были сериализуемыми.
Идея состоит в том, что он сериализует ваш объект, а затем десериализует его в новый объект. Преимущество заключается в том, что вам не нужно беспокоиться о клонировании всего, когда объект становится слишком сложным.
И с использованием методов расширения (также из исходного источника):
Если вы предпочитаете использовать новые методы расширения C # 3.0, измените метод так, чтобы он имел следующую подпись:
Теперь вызов метода просто делается
objectBeingCloned.Clone();
.РЕДАКТИРОВАТЬ (10 января 2015 г.) Я подумал, что я еще раз вернусь к этому, чтобы упомянуть, что недавно начал использовать (Newtonsoft) Json для этого, он должен быть легче и избегать накладных расходов на теги [Serializable]. ( NB @atconway указал в комментариях, что частные члены не клонируются с использованием метода JSON)
источник
typeof(T).IsSerializable
также верно, если тип помечен[Serializable]
атрибутом. Он не должен реализовыватьISerializable
интерфейс.Я хотел клонер для очень простых объектов, в основном из примитивов и списков. Если ваш объект из коробки JSON сериализуем, то этот метод поможет. Это не требует модификации или реализации интерфейсов клонированного класса, просто сериализатор JSON, такой как JSON.NET.
Также вы можете использовать этот метод расширения
источник
Newtonsoft.Json.JsonConvert
но это то же самоеПричина не использовать ICloneable это не потому , что он не имеет общий интерфейс. Причина не использовать это потому, что это расплывчато . Не ясно, получаете ли вы мелкую или глубокую копию; это до исполнителя.
Да,
MemberwiseClone
делает мелкую копию, но противоположностьMemberwiseClone
- нетClone
; это было бы, возможноDeepClone
, что не существует. Когда вы используете объект через его интерфейс ICloneable, вы не можете знать, какой тип клонирования выполняет базовый объект. (И комментарии XML не прояснят это, потому что вы получите комментарии интерфейса, а не комментарии к методу Clone объекта.)Обычно я просто делаю
Copy
метод, который делает именно то, что я хочу.источник
После большого прочтения о многих опциях, связанных здесь, и возможных решениях этой проблемы, я полагаю, что все опции суммированы довольно хорошо по ссылке Яна Р (все остальные варианты - их варианты), и лучшее решение предоставлено Pedro77 «S ссылка на комментарии вопрос.
Так что я просто скопирую соответствующие части этих двух ссылок здесь. Таким образом, мы можем иметь:
Лучшее, что можно сделать для клонирования объектов в C Sharp!
В первую очередь, это все наши варианты:
В статье «Быстрое глубокое копирование деревьями выражений » также представлено сравнение производительности клонирования с помощью деревьев сериализации, отражения и выражения.
Почему я выбираю ICloneable (то есть вручную)
Г-н Венкат Субраманиам (избыточная ссылка здесь) подробно объясняет почему .
Вся его статья основана на примере, который пытается быть применимым для большинства случаев, используя 3 объекта: Персона , Мозг и Город . Мы хотим клонировать человека, у которого будет свой мозг, но тот же город. Вы можете изобразить все проблемы, которые могут принести другие методы, описанные выше, или прочитать статью.
Это моя слегка измененная версия его заключения:
Надеюсь, эта реализация прояснит ситуацию:
Теперь рассмотрите возможность получения класса от Person.
Вы можете попробовать запустить следующий код:
Результат будет:
Заметьте, что если мы будем вести подсчет количества объектов, то клон, реализованный здесь, будет вести правильный подсчет количества объектов.
источник
ICloneable
для публичных членов. «Поскольку вызывающие функции Clone не могут зависеть от метода, выполняющего предсказуемую операцию клонирования, мы рекомендуем не реализовывать ICloneable в общедоступных API». msdn.microsoft.com/en-us/library/… Однако, исходя из объяснения, данного Venkat Subramaniam в вашей связанной статье, я думаю, что имеет смысл использовать в этой ситуации, пока создатели объектов ICloneable имеют глубокий понимание того, какие свойства должны быть глубокими и мелкими копиями (т.е. глубокая копия мозга, мелкая копия города)Я предпочитаю конструктор копирования клону. Намерение яснее.
источник
Простой метод расширения для копирования всех открытых свойств. Работает для любых объектов и не требует, чтобы класс был
[Serializable]
. Может быть расширен для другого уровня доступа.источник
Я только что создал проект
CloneExtensions
библиотеки . Он выполняет быстрое и глубокое клонирование с использованием простых операций присваивания, генерируемых компиляцией кода среды выполнения Expression Tree.Как это использовать?
Вместо того, чтобы писать свои собственные
Clone
илиCopy
методы с тоном назначений между полями и свойствами, заставьте программу сделать это самостоятельно, используя Expression Tree.GetClone<T>()
Метод, помеченный как метод расширения, позволяет просто вызвать его в вашем экземпляре:Вы можете выбрать то , что должно быть скопировано из
source
вnewInstance
использованииCloningFlags
перечисления:Что можно клонировать?
Следующие члены класса / структуры внутренне клонируются:
Как быстро это?
Решение быстрее, чем рефлексия, потому что информация о членах должна быть собрана только один раз, прежде чем впервые
GetClone<T>
будет использована для данного типаT
.Это также быстрее, чем решение на основе сериализации, когда вы клонируете более одного экземпляра одного типа
T
.и более...
Подробнее о сгенерированных выражениях читайте в документации .
Пример списка отладочных выражений для
List<int>
:}
что имеет такое же значение, как следующий код C #:
Разве это не так, как вы бы написали свой собственный
Clone
метод дляList<int>
?источник
У меня были проблемы с использованием ICloneable в Silverlight, но мне понравилась идея разделения, я могу отделить XML, поэтому я сделал это:
источник
Если вы уже используете стороннее приложение, такое как ValueInjecter или Automapper , вы можете сделать что-то вроде этого:
Используя этот метод, вам не нужно реализовывать
ISerializable
илиICloneable
на ваших объектах. Это часто встречается в паттерне MVC / MVVM, поэтому были созданы такие простые инструменты.посмотрите пример глубокого клонирования ValueInjecter на GitHub .
источник
Лучше всего реализовать такой метод расширения, как
а затем использовать его в любом месте решения
Мы можем иметь следующие три реализации:
Все связанные методы хорошо работают и были тщательно проверены.
источник
Краткий ответ: вы наследуете от интерфейса ICloneable, а затем реализуете функцию .clone. Клон должен сделать для каждого члена копию и выполнить глубокое копирование любого члена, которому это требуется, затем вернуть полученный объект. Это рекурсивная операция (она требует, чтобы все члены класса, который вы хотите клонировать, были либо типами значений, либо реализовали ICloneable, и чтобы их члены были либо типами значений, либо реализовали ICloneable и т. Д.).
Для более подробного объяснения клонирования с использованием ICloneable ознакомьтесь с этой статьей .
Длинный ответ «это зависит». Как уже упоминалось, ICloneable не поддерживается обобщениями, требует особых соображений для циклических ссылок на классы, и в действительности некоторые воспринимаются как «ошибка» в .NET Framework. Метод сериализации зависит от того, ваши объекты могут быть сериализуемыми, а они могут отсутствовать, и вы не можете контролировать их. В сообществе все еще много споров о том, что является «лучшей» практикой. На самом деле, ни одно из решений не является универсальным, подходящим для наилучшей практики для всех ситуаций, в которых изначально интерпретировался ICloneable.
Посмотрите эту статью Уголок разработчика для нескольких дополнительных вариантов (кредит Ян).
источник
Приветствия.
источник
РЕДАКТИРОВАТЬ: проект прекращен
Если вы хотите истинное клонирование в неизвестные типы, вы можете взглянуть на fastclone .
Это клонирование на основе выражений, работающее примерно в 10 раз быстрее, чем двоичная сериализация, и поддерживающее полную целостность графов объектов.
Это означает: если вы несколько раз ссылаетесь на один и тот же объект в вашей иерархии, клон также будет иметь ссылку на один экземпляр.
Нет необходимости в интерфейсах, атрибутах или любых других модификациях клонируемых объектов.
источник
Сохраняйте простоту и используйте AutoMapper, как уже упоминалось, это простая маленькая библиотека для отображения одного объекта на другой ... Чтобы скопировать объект на другой с тем же типом, все, что вам нужно, это три строки кода:
Целевой объект теперь является копией исходного объекта. Не достаточно просто? Создайте метод расширения для использования везде в вашем решении:
Метод расширения может быть использован следующим образом:
источник
Я придумал это, чтобы преодолеть недостаток .NET, связанный с необходимостью глубокого копирования List <T> вручную.
Я использую это:
И в другом месте:
Я попытался придумать oneliner, который делает это, но это невозможно, так как yield не работает внутри блоков анонимных методов.
Более того, используйте общий клонер List <T>:
источник
В. Почему я выбрал этот ответ?
Другими словами, используйте другой ответ, если только у вас нет узкого места в производительности, которое необходимо исправить, и вы можете доказать это с помощью профилировщика. .
В 10 раз быстрее, чем другие методы
Следующий метод выполнения глубокого клона:
И метод ...
Для максимальной скорости вы можете использовать Nested MemberwiseClone для создания глубокой копии . Это почти такая же скорость, как копирование структуры значений, и намного быстрее, чем (а) отражение или (б) сериализация (как описано в других ответах на этой странице).
Обратите внимание, что если вы используете Nested MemberwiseClone для глубокой копии , вы должны вручную реализовать ShallowCopy для каждого вложенного уровня в классе и DeepCopy, который вызывает все упомянутые методы ShallowCopy для создания полного клона. Это просто: всего несколько строк, см. Демонстрационный код ниже.
Вот выходные данные кода, показывающие относительную разницу в производительности для 100 000 клонов:
Использование Nested MemberwiseClone в классе почти так же быстро, как копирование структуры, и копирование структуры чертовски близко к теоретической максимальной скорости, на которую способна .NET.
Чтобы понять, как сделать глубокое копирование с использованием MemberwiseCopy, вот демонстрационный проект, который использовался для генерации времени выше:
Затем вызовите демо из основного:
Опять же, обратите внимание, что если вы используете Nested MemberwiseClone для глубокой копии , вы должны вручную реализовать ShallowCopy для каждого вложенного уровня в классе и DeepCopy, который вызывает все упомянутые методы ShallowCopy для создания полного клона. Это просто: всего несколько строк, см. Демонстрационный код выше.
Типы значений и типы ссылок
Обратите внимание, что когда дело доходит до клонирования объекта, существует большая разница между « struct » и « class »:
Смотрите различия между типами значений и ссылочными типами .
Контрольные суммы, чтобы помочь в отладке
Действительно полезно для отделения многих потоков от многих других потоков
Одним из отличных вариантов использования этого кода является подача клонов вложенного класса или структуры в очередь для реализации шаблона производитель / потребитель.
ConcurrentQueue
.На практике это работает очень хорошо и позволяет нам отделить множество потоков (производителей) от одного или нескольких потоков (потребителей).
И этот метод слишком быстр: если мы используем вложенные структуры, он в 35 раз быстрее, чем сериализация / десериализация вложенных классов, и позволяет нам использовать все потоки, доступные на машине.
Обновить
Очевидно, что ExpressMapper работает быстрее, если не быстрее, чем ручное кодирование, как описано выше. Возможно, мне придется посмотреть, как они сравниваются с профилировщиком.
источник
В общем, вы реализуете интерфейс ICloneable и внедряете Clone самостоятельно. Объекты C # имеют встроенный метод MemberwiseClone, который выполняет поверхностное копирование, которое может помочь вам для всех примитивов.
Для глубокой копии нет способа узнать, как автоматически это сделать.
источник
Я видел, как это реализовано через отражение. По сути, был метод, который итерировал бы элементы объекта и соответствующим образом копировал их в новый объект. Когда он достиг ссылочных типов или коллекций, я думаю, что он сделал рекурсивный вызов сам по себе. Отражение дорого, но сработало довольно хорошо.
источник
Вот глубокая копия реализации:
источник
Поскольку я не смог найти клонер, отвечающий всем моим требованиям в разных проектах, я создал глубокий клонер, который можно настраивать и адаптировать к разным структурам кода вместо того, чтобы адаптировать свой код для удовлетворения требований клонеров. Это достигается путем добавления аннотаций к коду, который должен быть клонирован, или вы просто оставляете код так, как он имеет поведение по умолчанию. Он использует рефлексы, кэширование типов и основан на более быстром . Процесс клонирования очень быстрый для огромного количества данных и высокой иерархии объектов (по сравнению с другими алгоритмами на основе отражения / сериализации).
https://github.com/kalisohn/CloneBehave
Также доступно как пакет nuget: https://www.nuget.org/packages/Clone.Behave/1.0.0
Например: следующий код будет DeepClone Address, но будет выполнять только поверхностную копию поля _currentJob.
источник
Генератор кода
Мы увидели много идей от сериализации до ручной реализации и отражения, и я хочу предложить совершенно другой подход с использованием генератора кода CGbR . Метод генерирования клона эффективен с точки зрения памяти и процессора и поэтому в 300 раз быстрее, чем стандартный DataContractSerializer.
Все, что вам нужно, это частичное определение класса,
ICloneable
а генератор сделает все остальное:Примечание: последняя версия имеет больше нулевых проверок, но я оставил их для лучшего понимания.
источник
Мне нравятся такие конструкторы копирования:
Если у вас есть еще что-то для копирования, добавьте их
источник
Этот метод решил проблему для меня:
Используйте это так:
MyObj a = DeepCopy(b);
источник
Это быстрое и простое решение, которое сработало для меня, не опираясь на сериализацию / десериализацию.
РЕДАКТИРОВАТЬ : требуется
Вот как я это использовал
источник
Следуй этим шагам:
ISelf<T>
соSelf
свойством только для чтения, которое возвращаетT
, иICloneable<out T>
, которое происходит отISelf<T>
и включает методT Clone()
.CloneBase
тип, который реализуетprotected virtual generic VirtualClone
приведениеMemberwiseClone
к переданному типу.VirtualClone
путем вызова базового метода клонирования, а затем делать все, что нужно для правильного клонирования тех аспектов производного типа, которые родительский метод VirtualClone еще не обработал.Для максимальной универсальности наследования классы, представляющие открытую функциональность клонирования, должны быть
sealed
, но наследоваться от базового класса, который в остальном идентичен, за исключением отсутствия клонирования. Вместо того, чтобы передавать переменные явно клонируемого типа, возьмите параметр типаICloneable<theNonCloneableType>
. Это позволит подпрограмме, которая ожидает, что клонируемое производноеFoo
будет работать с клонируемым производнымDerivedFoo
, но также позволит создавать неклонируемые производныеFoo
.источник
Я думаю, что вы можете попробовать это.
источник
Я создал версию принятого ответа, которая работает как с [Serializable], так и с [DataContract]. Прошло много времени с тех пор, как я написал это, но если я правильно помню, [DataContract] нуждался в другом сериализаторе.
Требуется System, System.IO, System.Runtime.Serialization, System.Runtime.Serialization.Formatters.Binary, System.Xml ;
источник
Хорошо, в этом посте есть очевидный пример с отражением, НО отражение обычно медленное, пока вы не начнете его правильно кэшировать.
если вы кешируете его правильно, то он клонирует 1000000 объектов на 4,6 с (измерено Watcher).
чем вы берете кэшированные свойства или добавляете новые в словарь и просто используете их
полный код проверки в моем посте в другом ответе
https://stackoverflow.com/a/34365709/4711853
источник
prop.GetValue(...)
все еще является отражением и не может быть кэширован. В дереве выражений оно скомпилировано, так что быстрееПоскольку почти все ответы на этот вопрос были неудовлетворительными или явно не работают в моей ситуации, я создал AnyClone, который полностью реализован с учетом и решил все потребности здесь. Мне не удалось заставить сериализацию работать в сложном сценарии со сложной структурой, и я
IClonable
не идеален - на самом деле это даже не нужно.Стандартные атрибуты игнорирования поддерживаются с помощью
[IgnoreDataMember]
,[NonSerialized]
. Поддерживает сложные коллекции, свойства без сеттеров, поля только для чтения и т. Д.Я надеюсь, что это поможет кому-то еще, кто столкнулся с теми же проблемами, что и я.
источник
Отказ от ответственности: я автор упомянутого пакета.
Я был удивлен тем, как в лучших ответах на этот вопрос в 2019 году все еще используются сериализация или рефлексия.
Сериализация ограничивает (требует атрибутов, определенных конструкторов и т. Д.) И очень медленная
BinaryFormatter
требуетSerializable
атрибута,JsonConverter
требует безпараметрического конструктора или атрибутов, ни обрабатывает поля или интерфейсы только для чтения очень хорошо, и оба в 10-30 раз медленнее, чем необходимо.Деревья выражения
Вместо этого вы можете использовать деревья выражений или Reflection.Emit для генерации кода клонирования только один раз, а затем использовать этот скомпилированный код вместо медленного отражения или сериализации.
Сам столкнувшись с проблемой и не найдя удовлетворительного решения, я решил создать пакет, который делает именно это и работает с каждым типом и почти так же быстро, как пользовательский код .
Вы можете найти проект на GitHub: https://github.com/marcelltoth/ObjectCloner
Применение
Вы можете установить его из NuGet. Либо получите
ObjectCloner
пакет и используйте его как:или если вы не возражаете загрязнять ваш тип объекта расширениями,
ObjectCloner.Extensions
напишите также:Представление
Простой эталон клонирования иерархии классов показал производительность примерно в 3 раза быстрее, чем при использовании Reflection, в 12 раз быстрее, чем сериализация Newtonsoft.Json, и в 36 раз быстрее, чем рекомендовано
BinaryFormatter
.источник