C# - быстро склонировать несложный объект
Есть один или пачка POCO, пусть даже с какой-то иерархией наследования. Какой способ быстро склонировать эту пачку в наше время?
Раньше народ клепал свои методы на каждый такой класс. Но это надо не забыть исправить такой метод, если исправляется какое-то свойство в клонируемом типе. Да и больше подходит для кастомного копирования с изменениями (т.е. это уже не копирование, а сериализация будет), а не для простого "один к одному".
Потом варианты через сериализаторы - если не сотни мегабайт объектов так клонировать, то пойдёт. Особенно, если не нужно расставлять атрибуты сериализации - т.е. подойдут всякие JSON-сериализаторы.
Ещё с деревьями выражений находил примеры. Но они какие-то замудрёные. Хотя и говорят, что это быстрее всего.
Есть ещё разные фреймворки, но по-моему они внутри делают что-то из вышеперечисленного.
Тут чел собрал разные методы, при этом топит за ручное клонирование. Но то 2012 год. Да и уже говорил, что это скорее для сложного клонирования не "один к одному" и разных не-POCO вещей.
С сериализаторами просто - Serialize, Deserialize, и вот у тебя полная копия POCO. Собственно, сейчас с ними клонирую.
В шарпе есть IClonable.
Который делает примерно ничего, кроме объявления контракта, что тут что-то клонируется - всё должен делать ты сам, причём ручками. Мне-то надо, чтобы работать поменьше, а результата побольше.
Чтобы не забыть клонировать новые поля можно написать юнит-тест и рефлекшенами считать количество (и названия) полей. Этот прием также можно использовать для тестирования Equal.
Что-то невероятно замудрённое. ))
Короче, продолжаю юзать это - не нужно никаких атрибутов дописывать, интерфейсы реализовывать, и вообще ничего не нужно - тупо на любой POCO класс применяется из коробки:
var temp = JsonConvert.SerializeObject(original);
var copy = JsonConvert.DeserializeObject(temp);
Короче, продолжаю юзать это - не нужно никаких атрибутов дописывать, интерфейсы реализовывать, и вообще ничего не нужно - тупо на любой POCO класс применяется из коробки:
Как оно с private свойствами работает?
Падает ли, если в дереве объектов окажется циклическая зависимость?
Универсального метода не будет видимо. Выбираем по ситуации
https://www.wwt.com/article/how-to-clone-objects-in-dotnet...
https://learn.microsoft.com/en-us/dotnet/api/system.object...
https://www.codeproject.com/Articles/23832/Implementing-De...
Как оно с private свойствами работает?
Падает ли, если в дереве объектов окажется циклическая зависимость?
Для приватных надо атрибуты расставлять. Или настройки для всего типа делать (т.е. тоже атрибут).
Цикл можно заигнорить.
Другой объект в составе сериализуемого можно сериализовать со всеми его данными, а можно лишь ссылку на него.
вот это ты смачно в лужу пукнул.
IClonable и был придуман для того, что хочет Алекс.
Никто кроме самого объекта не может знать как надо клонироваться (и надо ли вообще). Так что правильный путь - реализация IClonable. Все остальное - костыли, которые посыпятся в самый неподходящий момент.
короче нет серебряной пули (((
Плюс у тебя во второй ссылке реестр идёт про библиотеку Newtonsoft
Конечно пулит нет. Я же упростил и говорил про POCO.
Эту либу я и раньше юзал. МСовская была не очень раньше. Они её дорабатывают, но всё равно не дотягивает до json.net. Но суть та же - клонирование несложных объектов через сериализацию до сих пор самый простой способ клонирования, чем вводить свои копировщики, интерфейсы и прочее. Если будете свои копировщики вводить, то столкнётесь с теми же проблемами, что и при сериализации - как ссылки обрабатывать, как циклические зависимости и прочее. И придёте к тем же решениям - вводить нотацию для ссылок или копировать как есть с дублированием данных, опция для игнора циклов, и т.п. Только сами вы это будете неделями и месяцами разрабатывать со всеми сопустствующими ошибками. А сериализаторы уже готовы и отлажены. Проще добить свои классы до сериализации (расставить атрибуты) и воспользоваться ей, чем вводить свои копировщики.
IClonable - старый дотнетовский интерфейс, введённый ещё до появления других удобных инструментов клонирования. Он никак не помогает клонировать, т.к. не содержит никаких вспомогательных инструментов, а просто декларация, широко используемая в самом Дотнете. Написать интерфейс клонирования с одним едиственным методом без реализации - что может быть проще. А вот реализовать... Атавизм, как и класс HashTable и многие другие, оставленные для совместимости.
вот это ты смачно в лужу пукнул.IClonable и был придуман для того, что хочет Алекс.
Никто кроме самого объекта не может знать как надо клонироваться (и надо ли вообще). Так что правильный путь - реализация IClonable. Все остальное - костыли, которые посыпятся в самый неподходящий момент.
С интерфейсом проблема в том, что он декларирует наличие реализации, но никак не отслеживает её актуальность при изменении типа. Тип изменился, и разработчик должен посмотреть, какие другие части этого типа тоже должны измениться. Со сложными типами это море работы, и всё равно можно что-то упустить. Тогда как атрибуты находятся там же, где и изменяющиеся вещи в типе - прямо над ними. Так что забыть изменить сериализацию сложнее - это надо специально атрибуты игнорировать.
Предлагают обложиться тестами, ага. Только как в тестах отслеживать, что там теперь должны быть новые члены? Создавать тестовые объекты через рефлексию, проходя по всем текущим свойствам и сравнивая с объектом после сериализации? Вы тогда тесты будете разрабатывать дольше, чем сами типы, а их сложность может потребовать тесты над тестами. А по заветам отцов-основателей, тесты должны быть максимально простыми.
Так что правильный путь - реализация IClonable. Все остальное - костыли, которые посыпятся в самый неподходящий момент.
+1. И "копирующий конструктор".
И "копирующий конструктор".
-----
В С++ - да.
В С# - уже нет.
Насколько Я помню среда .НЕТа пользует IClonable по своему усмотрению, а про "копирующий конструктор" она не в курсе.
Посмотрите на количество имеющих этот интерфейс внутренних классов Дотнета (список "Derived") -
https://learn.microsoft.com/en-us/dotnet/api/system.iclone...
Все они юзают его, а не какой-то конструктор-копировщик. Последний - действительно пришёл из плюсов.
Этот интерфейс можно использовать, если у вас какая-то сложная схема копирования объектов, требующая логики реализации, которую нельзя просто атрибутами обозначить. Ну и если хотите синхронизироваться с другими системами, которые на этот интерфейс рассчитывают. А мне для простых объектов неохота ничего писать, а охота как можно быстрее. А уж если эти объекты и так должны сериализироваться - т.е. я их для этого уже подготовил - то тем более грех не воспользоваться уже готовым инструментом.
Тогда либо игнорить циклы, либо настраивать модели, расставляя атрибуты, чтобы часть полей не сериализовывалась. Но это уже не просто вызвать пару методов для клонирования.
если часть полей не клонируется, то это уже не клонирование.