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...
Все они юзают его, а не какой-то конструктор-копировщик. Последний - действительно пришёл из плюсов.
Этот интерфейс можно использовать, если у вас какая-то сложная схема копирования объектов, требующая логики реализации, которую нельзя просто атрибутами обозначить. Ну и если хотите синхронизироваться с другими системами, которые на этот интерфейс рассчитывают. А мне для простых объектов неохота ничего писать, а охота как можно быстрее. А уж если эти объекты и так должны сериализироваться - т.е. я их для этого уже подготовил - то тем более грех не воспользоваться уже готовым инструментом.
Тогда либо игнорить циклы, либо настраивать модели, расставляя атрибуты, чтобы часть полей не сериализовывалась. Но это уже не просто вызвать пару методов для клонирования.
если часть полей не клонируется, то это уже не клонирование.
Это вполне нормально.
Те самые ссылочное поля бессмысленно клонировать - их надо инициировать ссылками на объекты.
Я точно не знаю, как эта ситуация обрабатывается. Ссылочные данные могут быть сериализованы ссылками в той либе. Но ссылке в джейсоне не стандартизированы, поэтому каждая либа их делает по-своему.
А циклы - можно не сериализовать. Но непонятно, что это значит - вообще поле пропускается, или на глубину 1 всё же сериализуется. Тестировать надо. По идее, если ссылочные типы просто заменять ссылками, то циклов возникать не должно - мы просто пишем для ссылочного поля его ссылочный айди (в нотации сериализованного джейсона - см. выше мой линк на ту либу), и дальше вглубь это поле не сериализуем. Всё, от цикла избавились.
Но опять же, логика присвоения эти ссылочных айди - чтобы на одну ссылку разные айди не присвоил. Есть ли там сравнение адресов ссылок, или просто каждому следующему ссылочному полю идёт присвоение инкрементированного айди.
Я хочу сделать у своего проекта in-memory базу данных. Просто таблицы без связей, которые при старте приложения загружаются из текстовых файлов. Таблицы эмулируются банальными словарями, где айдишники это ключи, а значения - объекты. И потом я из этой "базы данных" читаю строки. Когда с обычной базой данных работаешь, ты создаёшь копию такого объекта (строки) у себя в приложении. А в моём варианте, чтобы не копировать ссылку из моих словарей на один и тот же объект, нужна операция клонирования. И т.к. связей между "таблицами" у меня нет, то и циклов у меня нет. В обычных БД, в ORM, вопрос с цикличностью решается ленивой загрузкой, насколько я знаю. Ну и программист сам определяет, насколько глубоко он там связи между таблицами запросит.
Чуваки из игрового мира не привыкли заморачиваться базами данных на клиенте. И в чём-то они правы - возиться ещё с ними. А в Дотнете такой удобный механизм LINQ to objects, да ещё типизированный - очень похожий на LINQ to entities, который используется для БД. И раз уж всё равно объекты сериализовать надо (сохранения, загрузки состояний), то убью двух зайцев одним ударом.
А в моём варианте, чтобы не копировать ссылку из моих словарей на один и тот же объект, нужна операция клонирования
всё равно не доходит. Если это всё реадонди, то какая разница?
Я хочу сделать у своего проекта in-memory базу данных.
типа NoSQL? А зачем то самому делать? Пару строк и все работает. https://www.infoworld.com/article/3672154/how-to-use-ef-co...
Ну или типа этого https://www.litedb.org/
типа NoSQL? А зачем то самому делать? Пару строк и все работает. https://www.infoworld.com/article/3672154/how-to-use-ef-co...
Ну или типа этого https://www.litedb.org/
Я не знаю, что такое nosql, но у меня есть коллеции объектов в памяти, я их сериализую в текстовый человекочитаемый файлик, потом могу подправить его хоть в Блокноте, потом сохранить и десериализовать снова в своё приложение. Если эта штука по последней ссылке так может (особенно человекочитаемый файлик для данных таблицы), то посмотрю её. Но они предлагают какую-то свою Студию юзать для редактирования файлов. Похоже, что файлы таблиц там не человекочитаемые. И редактировать в разных текстовых редакторах я их не смогу.
Я не знаю, что такое nosql
не страшно
https://skillbox.ru/media/code/nosql-chto-eto-za-bazy-dann...
есть коллекции объектов в памяти, я их сериализую в текстовый человекочитаемый файлик
Ломит редакторы делать? Это разве удобно для пользователей?
Но тоже не вижу зачем клонирование...
Делаем словарик <ObjectType, Object>
Делаем цикл по словарю сохраняем каждый в json файл, где ключ имя файла. Для чтения наоборот: цикл по файлам, создаём объекты.
Если не огромная коллекция то можно и всё сразу выгрузить в один файл.
litedb - не для этого
Я не знаю, что такое nosqlне страшно
https://skillbox.ru/media/code/nosql-chto-eto-za-bazy-dann...
Да не, не так. - Я не хочу знать, что такое nosql. )))
Делаем цикл по словарю сохраняем каждый в json файл, где ключ имя файла. Для чтения наоборот: цикл по файлам, создаём объекты.
Если не огромная коллекция то можно и всё сразу выгрузить в один файл.
Не, мне лучше, чтобы на одну таблицу один файл. Мне проще даже не джейсон, а csv. Типа такого
id,name,race,age
0000,Alex,human,50
0001,Malex,thuman,100500
Но для csv надо будет писать свой сериализатор-десериализатор, а для джейсона уже всё есть.
Редактор пока не нужен. Ну или хватит текстового. Ну или тупо список объектов в датагрид загрузить с возможностью редактирования их свойств через редактирование ячеек датагрида.
чтобы на одну таблицу один файл
Что значит одна таблица?
Но для csv надо будет писать свой сериализатор-десериализатор, а для джейсона уже всё есть.
ну вот именно
Делов то на полчаса или и того меньше.
Я не хочу знать, что такое nosql.
ну тут сорри, ничем не могу помочь. Каждый решает для себя. Мне бы было некомфортно.
in-memory базу данных
-----
И чем тебя обычный DataSet не устраивает?
Таблицы эмулируются
-----
Нахрена?
Таблица, со всей функциональностью, уже есть.
Делаешь ее строго типизированной и забываешь об 95% проблем.
Это вполне нормально.
Те самые ссылочное поля бессмысленно клонировать - их надо инициировать ссылками на объекты.
ничего не понял
Как сделать клонирование ссылочных атрибутов объекта?
Он имеет ввиду, что при клонировании ссылки на объект восстанавливаются как ссылки, а не как объекты со своими полями. Но при сериализации и десериализации ссылки теряются, т.к. уже может не существовать объект, на который ссылаются. Либа по джейсону, что я предлаг, может сохранять ссылки.
Сама идея сериализации - мы вытаскиваем какой-то объект или граф объектов из контекста и разрываем все свази этого графа объектов с контекстом. При десериализации мы восстанавливаем данные графа, но не его связи с контекстом, т.к. обычно уже сам контект либо поменялся, либо уже не существует. Поэтому пре сериализации нет смысла сохранять все ссылки, а только те, что связывают объекты в сериализуемом графе.
Мне надо создать копию объекта без потери контекста, т.к. я генерю её тут же - контекст не успевают измениться. Но я создаю копии лишь данных, а не ссылок. Новую копию я ввожу в контекст, создавая для неё свои ссылки, а не копируя ссылки оригинала. В частности, я генерю для копии новый айдишник. И теперь это копия лишь в смысле данных, но ссылаться на неё нужно уже по своему уникальному айдишнику.
Таблицы эмулируются
-----
Нахрена?
Таблица, со всей функциональностью, уже есть.
Делаешь ее строго типизированной и забываешь об 95% проблем.
Мне надо, чтобы сохранённый вариант был человекочитаемый и достаточно легко редактируемый в текстовом редакторе типа Блокнота. Джейсон не нравится тем, что он некомпактный - даже несколько десятков записей будут требовать много скроллинга для просмотра. А вот формат csv очень даже компактный - можно эти несколько десятков уместить на один экран.
А вот формат csv очень даже компактный
а не пробовали его реально редактировать в блокноте? когда пара сотен строк, и пара десятков столбцов с данными разной длины.
Тогда согласен. Просто я думаю о своём, а у меня данные компактные. Единственное что, в csv вроде только один символ-разделитель можно сделать. И даже если это будет таб, то столбцы всё равно неровными могут быть. Два таба в качестве разделителя вроде нельзя. Хотя, там стандарта на разделитель нет - как считывающий код сделаешь, так и будет... Вобщем, csv - компромиссный вариант. Но он мне в любом случае не нужен, т.к. генерить его надо ручками, а не автоматом натравил либу на класс и она всё сериализировала почти как надо, без расстановки атрибутов и громождения конфигураторов.
Мне надо, чтобы сохранённый вариант был человекочитаемый и достаточно легко редактируемый в текстовом редакторе типа Блокнота.
А вот формат csv очень даже компактный - можно эти несколько десятков уместить на один экран.
Уместить-то может быть и можно (хотя зависит от длины хранимых данных), а вот найти нужное поле или не дай бог редактировать - это трэш.
Датасет же ин-мемори коллеция данных, а не на диске. Т.е. к нему нужно иметь внешнее хранилище так и так. И ещё нужна отдельная прога для поддержки датасетов и редактирования через них данных - чтобы хотя бы грид с возможностью редактирования показала.
И датасет это не один файл, а кучка (минимум 4, вроде, на которые разваливается .xsd), описывающих один датасет.
У датасета и дататейбл из всей нужной функциональности - поддержка IList, IEnumerable и сериализации в XML. Сериализовать POCO в XML или джейсон можно и так из коробки. И загнать свои данные во что-то IEnumerable или IList я тоже легко могу - банальный словарь. И затем запрашивать их с полной функциональностью sql-подобных запросов LINQ to objects. Тогда зачем мне вся эта инфраструктура датасетов, если поддерживать её сложнее, и редактировать быстро извне в простой сторонней программе типа Блокнота нельзя?
Датасет же ин-мемори коллеция данных, а не на диске.
Именно так.
Т.е. к нему нужно иметь внешнее хранилище так и так.
Храни как хочешь. Хоть в XML :)
И ещё нужна отдельная прога для поддержки датасетов и редактирования через них данных - чтобы хотя бы грид с возможностью редактирования показала.
Зачем? :) Если в ТЗ такого нет, то не нужна. А если в ТЗ есть, то ты и со своими классами должен эту прожку написать.
И датасет это не один файл, а кучка (минимум 4, вроде, на которые разваливается .xsd), описывающих один датасет.
WriteXML создает одни XML файл. А ReadXML (внезапно) этот XML читает и воссоздает DataSet.
Тогда зачем мне вся эта инфраструктура датасетов
Если тебе нужна лайт BD, то DataSet оптимальное решение. А еще DataSet можно подружить с твоим любимым EF и вообще кайф :D
редактировать быстро извне в простой сторонней программе типа Блокнота нельзя?
Ну конечно можно. XML вполне себе поддается редактированию блокнотом :) А еще можно там искать данные при помощи XPath
и менять только то, что надо :D :D :D
чтобы сохранённый вариант был человекочитаемый
------
И какие проблемы?
Подставляешь нужный сериалайзер и получаешь что требуется.
то столбцы всё равно неровными могут быть
-----
Какая хрень...
Дампишь дататабле с дополнением полей до полного и получаешь абсолютно ровненькие колонки...
из всей нужной функциональности
-----
Из всей известной тебе функциональности.
Там вполне работает LINQ, кроме этого - фильтрация по SQL.
Тогда зачем мне вся эта инфраструктура датасетов, если поддерживать её сложнее, и редактировать быстро извне в простой сторонней программе типа Блокнота нельзя?
-----
Это ограничения твоих знаний...
Если тебе нужна лайт BD, то DataSet оптимальное решение. А еще DataSet можно подружить с твоим любимым EF и вообще кайф :D
Мне нужна ещё более лайт, чем датасеты - без связей между таблицами. Чем пачка List<T> или Dictionaty<T,T> не лайт ин-мемори ДБ без связей? Просто она не relational. И EF не нужен, т.к. всё и так типизировано. ))
Вопрос лишь в том, как хранить на диске. С датасетами - ок, можно писать-читать XML. Но и List<T> можно сериализовать в джейсон без проблем, если там POCO без связей.
Теперь другой вопрос - мне надо ещё и сериализовать граф объектов (состояние программы). Тут датасеты не нужны, а сераилазация снова в деле. Итого, для разных задач могу использовать лишь одну либу (например, для сериализации в джейсон), или разные либы (для сериализации в джейсон и для простейшей БД).
Посмотрел в Unity 3d - вроде, System.Data.dll подключается. Ок, можно оставить на заметку.
Посмотрел best practices в других небольших игровых проектах - не юзают люди БД. Хранят всё в джейсоне или YAML, редко в XML. Потом всё грузят в память и читают из памяти как из хранилища (базы данных). Там сама идея, что зависимости тебе нужны лишь при работающей программе - ты грузишь объекты из БД и создаёшь связи между ними. А в самой БД связи не нужны. А если хочешь состояние со связями сохранить, то сериализуешь, а не в БД сохраняешь. Чтобы в БД сохранить состояние, это надо, чтобы все объекты состояния присутствовали в БД в виде таблиц со связями. А это зачастую не нужно.
Вопрос лишь в том, как хранить на диске.
Опять не понимаю. А зачем вообще нужно сохранять на диске да и еще в удобочитаемом виде?
Откуда данные берутся вначале? Отчего нельзя данные редактировать сразу в проге?
Первое данное я создам в коде и сериализирую на диск в виде джейсона скорее всего. Потом вручную буду редактировать джейсон - добавлять новые данные. Там же просто массив будет, так элементы массива копипастой добавляешь и редактируешь.
В программе нет редактора данных. Отображение есть, а редактора нет. В теории можно сделать, но там стандартного грида из коробки нет, а эмулировать списком и по отдельности поля редактировать - муторно. В принципе, всё можно сделать, но мне проще не заморачиваться этим, а редактировать извне в каком-нибудь блокноте.
Потом вручную буду редактировать джейсон - добавлять новые данные
А в чём разница добавлять или редактировать?
А что то типа этого чем не нравится?
Это для редактора, не для рантайма. Редактор в Юнити - это когда делаешь игру, а рантайм - когда её запускаешь.
Да в принципе много можно чего найти. Бесплатного и платного. При желании на Юнити можно сделать какой угодно датагрид с привязками и прочим. Ну будет сколько-то стоить - можно заплатить. Просто мне это сейчас не надо. Я хочу как можно быстрее и проще избавиться от рутины и делать то, что интересно. Проще добавить пару объектов в коде, сохранить, и потом раз в неделю добавлять ещё в качестве теста по паре объектов в текстовом файле, чем писать самому какой-то сложный контрол для редактирования в игре, или искать подобный уже готовый и его осваивать.
Если захочу не вручную, то можно что-то типа такого использовать.
ааа, так для себя и юнити. Конечно, не нужен редактор. Консоль приложение для генерации файла вполне достаточно будет, думаю
Ну и конечно никаких свойств с неопределёнными типами, типа интерфейсов, object и прочего.
Я щас пытаюсь организовать в своих классах как я хотел. Как я раньше говорил - скормил объект сериализатору и всё - нет. Нужно придерживаться определённых правил при проектировании классов. Например, если конструкторов много разных, и есть по-умолчанию (без параметров), то нужно повесить атрибут сериализации на конструктор со всеми необходимыми параметрами, а если такого конструктора нет - создать его. Иначе при десериализации получишь объект с дефолтными полями (будет использован конструктор без параметров). Ну, это завязано на конкретный сериализатор. Можно через всякие дата контракты, но это муторнее.
Ещё раз, у меня такая идея. Есть импровизированная база данных в виде файликов (текстовые - джейсоны, csv или ещё что-то - неважно). Один файлик - одна таблица. Связей в базе данных между таблицами нет - это просто хранилище отдельных классов объектов. Скажем так, дефолтных параметров этих объектов. Там уникальные идентификаторы - текстовые поля (названия этих классов объектов). Например, две таблицы с идентификаторами "name"
таблица Characters:
className Warrior, attack 10, defence 5
className Archer, attack 5, defence 3
...
таблица Items:
className Knife, damage 2, price 400
className Sword, damage 5, price 1200
...
В базе данных может быть только один объект с уникальным именем класса, но в программе их может быть несколько - можно создать несколько Warrior, которые поначалу могут быть даже с одинаковыми параметрами, но потом могут начать различаться. Поэтому это уникальные Warriors, и им нужны свои идентификаторы - я выбрал инты, и затолкал их всех в словари , где T - тип объекта из файла (Warrior, Item, etc.). А в будущем нужно ещё добавить и понятные текстовые имена для персонажей однинаковых классов, например.
При запуске программы все таблицы из базы данных загружаются в виде обычных списков: List characterTable, List itemTable. Т.е. в них хранятся образцы объектов с дефолтными параметрами. И вот, чтобы создать уникальный объект из списка с образцами и добавить его в словарь, мне надо его склонировать. Т.е. я делаю так
var newCharacter = characterTable.FirstOrDefault(c => c.Name == name)
затем клонирую newCharacter через сериализацию-десериализацию, а потом приписываю newCharacter уникальный интовый идентификатор, и сохраняю его в словаре .
А потом эти уникальные объекты в словарях нужно ещё сохранять через сериализацию и потом восстанавливать их.
Т.е. сериализация служит для двух вещей, выделенных жирным: клонирование объектов и сохранение состояния программы. И классы, которые можно сериализовать, нужно специально проектировать. В принципе, ничего особенного, но порасставлять атрибуты придётся.
Вобщем, я эмулирую СУБД и ORM, только очень просто. Использовать настоящую СУБД нельзя, т.к. Юнити их не поддерживает, а заниматься установкой муторно. Все в небольших проектах делают это тупо в текстовых файлах, чтобы не заморачиваться, тем более что списки дефолтных объектов обычно небольшие, и применение для этого СУБД не несёт никакого преимущества. А ещё желательно иметь возможность редактировать эти списки в самом простом подручном редакторе (типа Блокнота). Вы же для СУБД тоже имеете специальную программу для редактирования - та же SQL Server Management Studio это нехилая такая программулина. А без неё будете в командной строке таблицы редактировать и выводить.
говнокод какой-то.
почему не сделать чот типа Repository или Factory, которая при инициализации запоминает, что Warrior‘а нужно создавать со свойствами attack 10, defence 5?
В Repository делаешь функцию T GetInstance<T>() where T : Character и все чистенько без дрочки с серей и десерей.
Я так и делаю - у меня есть класс репы, который всё это и делает. А роль фабрики выполняет клонирующий метод.
Свойства attack 10, defence 5 надо где-то хранить, а ещё редактировать. Не в коде же? Поэтому вот во внешних файликах, эмулирующих таблицы.
Ещё раз, была бы легко доступна нормальная СУБД, я бы её использовал. Но в Юнити всё сделано, чтобы использовали их продукты и привязывались к их движку, а всё постороннее интегрируется сложно. Так все делают, не только они.
В принципе, подошли бы наверное мини СУБД из ссылок АлексНека, если весь движок это одна ДЛЛка. Но они там непонятно в каких файлах хранят данные. А мне нужно простые для редактирования.
нифига ты не так делаешь.
у тебя список эталонных объектов и каждый раз ты берешь один и клонируешь его
Ага, в проге есть объекты, которым нужно сохранять состояние и создавать их на основе начальных данных.
И процесс создания объекта назван "клонированием". Ну да ладно, можно и так посмотреть, хотя это должны быть два различных объекта.
И в сериализации когда то будет проблема обновления объекта.
ну и еще если у тебя по условию задачи в файлике один хер каждый тип только один раз записан, то почему его свойства не прописывать в стандартном конструкторе класса?
Ты сначала объяснял по-другому, якобы тебе нужно инициировать много перосонажей одного типа.
В Repository делаешь функцию T GetInstance<T>() where T : Character и все чистенько без дрочки с серей и десерей.
Ну а сами-то функции что делают? Весь "говнокод" как раз в них и спрятан будет. У меня тоже самое.
нифига ты не так делаешь.
у тебя список эталонных объектов и каждый раз ты берешь один и клонируешь его
А какая разница, как ваш GetInstance создаёт этот инстанс? Хочу и клонирую - я же расписал, как, зачем и почему так делаю. Весь "говнокод" за этим методом спрятан. Будет СУБД, поменяю на СУБД, а метод останется тем же. Всё по фен-шую, но пока без СУБД.
И в сериализации когда то будет проблема обновления объекта.
Конечно будет. И её никак не решить по-нормульному, чтобы все острые углы обходить. Обычно обвешивают атрибутами и дата контрактами, которые отсутствующие поля игнорируют,или оставляют, но помечают как устаревшие и неиспользуемые, новые добавленные поля дефолтят, или что-то подобное. Сохранённые состояния нужно тоже обновлять. Или забыть про них и продолжить с новыми. Так поступают везде - настройки программы в БД, сохранения из игр, несовместимые с новыми версиями. Области разные, а подход один.
ну и еще если у тебя по условию задачи в файлике один хер каждый тип только один раз записан, то почему его свойства не прописывать в стандартном конструкторе класса?
Ты сначала объяснял по-другому, якобы тебе нужно инициировать много перосонажей одного типа.
Так и осталось - нужно инициировать много персонажей одного типа. Для этого клонирую их из списка, созданного из файлика.
В конструкторе хардкодить - это в исходниках значит. А мне нужны внешние конфигурационные файлы, чтобы легко можно было в блокнотике подправить.
Во! Назову это конфигурационными файлами. А то что-то я сущности придумываю новые, а по сути это конфиги и есть.
В Repository делаешь функцию T GetInstance() where T : Character и все чистенько без дрочки с серей и десерей.
Я может не понял, что вы сказали, но нужно не ссылку на объект из репы получить, а копию объекта - т.е. скопировать все поля. Т.е. склонировать. Это надо либо вручную писать для всех полей операции копирования, либо через сериализацию-десериализацию автоматически (ну, расставив пару-тройку атрибутов). При этом при изменении состава полей сериализацию проще поддерживать - по сути надо лишь отредактировать конструктор с параметрами. Этот конструктор со всеми параметрами - аналог копировщика из С++.
А сериализация для клонирования используется ещё и потому, что я её же использую и для сохранения состояния объектов. Если бы без этого, то можно было бы просто написать свой конструктор-копировщик. А так - немного меньше работы.
Так и осталось - нужно инициировать много персонажей одного типа. Для этого клонирую их из списка, созданного из файлика.
тогда почему в твоём файле не прописать сразу всех персонажей?
Типа
Вася - Воин - меткость 100
Петя - воин - меткость 200
Кроме персонажей, которые существуют в единственном экземпляре, есть ещё враги, которых может быть много с одними и теми же параметрами. А кроме того, надо бы подумать, как и персонажей тоже клонировать. Скажем, есть базовый типа "воин", и игрок может создать себе отряд из них, но разбросать им дополнительные характеристики к базовым. Это типичная возможность таких игр с незапамятных времён. Ну и ещё неплохо бы предусмотреть возможность клонировать любого текущего персонажа или врага - на какую-нибудь будущую игровую механику с клонированием. А если заложить возможность сериализации для сохранения состояния, то клонирование получается дополнительно почти автоматически. Нужно только добавить генерацию уникального айдишника. Мой метод клонирования так и делает.
существуют в единственном экземпляре, есть ещё враги, которых может быть много с одними и теми же параметрами
Имеется ввиду класс врагов. Скажем, есть "воин", есть "крутой воин", и у них разные базовые характеристики. Но я могу пойти ещё дальше, и добавить при создании врага из базовых характеристик небольшой разброс, чтобы клоны немного отличались. Это такая особенная игровая механика. Не так уж много где это реализовано - насколько я знаю, японцы вообще этим не заморачиваются. А у меня будет особенность. Ну и по-любому это всё надо добавлять в коллекцию уже созданных и существующих в игровом мире врагов с уникальными идентификаторами. Вот поэтому механика клонирования и нужна.
Ещё раз, если сохранение состояния через сериализацию даёт возможность сохранять весь граф объектов с уникальными параметрами, то не грех воспользоваться этим для клонирования, чтобы не писать отдельные клонирующие методы, которые будут делать то же, что и сериализатор. Мой клонирующий метод будет просто сериализовать-десериализовать любой уже существующий объект с помощью любого готового фреймворка (в данный момент это Json.NET), и добавлять уникальный айдишник.
То, что я в начале говорил, это была общая идея. По мере реализации она обрастает подробностями. Но общее описание не изменилось - клонирую из списка базовых конфигураций. Отличие от типичной БД в том, что обычно в базах данных хранят уникальные объекты, а не их классы. У меня это будет храниться в сериализованном виде в файле, т.к. сохранения состояния должны быть легко портабельными (скинул файлик на флешку, или передал по сети).
Вы когда-нибудь делали программу с сохранением состояния? Не просто конфиги или ненадолго один объект в сессии сохранить, а скажем все открытые окна, вкладки и значения в них? Или скажем открыл окно, вбил значения, закрыл, а вбитые значения не стёрлись, а запомнились, и при открытии такого окна снова, то же самое было бы на тех же местах? И чтобы можно было в любой момент засейвить состояние приложения и загрузить потом тоже в любой момент? Это сложнее, чем просто сохранять некоторые настройки в конфигах. Это надо изначально классы объектов и окон писать с возможностью сохранения состояния. Вот Студию вы закрыли, открыли, а все вкладки, инструменты и даже позиция курсора запомнились и открылись так, как были.
Хотя, пример с сохранением состояний окон наверное не очень. Лучше подходит какой-нибудь редактор - графики или там звука. Свои настройки такая программа хранит в конфигах, но весь граф объектов, представляющий картинку (включая слои, фильтры, их настройки и прочее) или звуковую композицию, должен поддерживать сохранение и загрузку состояния - т.е. быть сериализуемым.
Ну а если спроектировано всё с сохранением состояния, то клонирование через сериализацию получается почти из коробки.
ну ок, если все равно сериализация нужна, то ладно - можно так сделать.
и даже позиция курсора запомнились и открылись так, как были
-----
Угу... вот только пока Студия была в оффлайне кто шаловливыми пальчиками поковырялся в редактировавшихся файликах...
Дальше пояснять?
Тогда дефолтная позиция. Также, как и при десериализации, когда кто-то поправил руками джейсон там или исходный класс, и теперь между ними несоответствие.
Большие многомиллиардные корпорации забивают на все эти мелкие проблемы маленьких людей, и если что не соответствует, то говорят просто обновиться и забить на старьё - старые сейвы в новых обновлениях могут не работать, старые конфиги в новых версиях ПО также, и т.д. А вы тут, мелкие сошки, с доходом на всю контору как пара зарплат менеджеров ФААНГов, стараетесь все-все нюансы учесть, вплоть до "а если метеорит упадёт, тогда что? в вашем ПО есть защита от этого?". Проще надо быть. )))