русский
Germany.ruForen → Архив Досок→ Programmierung

C# - быстро склонировать несложный объект

2158  1 2 3 4 alle
alex445 коренной житель08.10.23 19:54
08.10.23 19:54 

Есть один или пачка POCO, пусть даже с какой-то иерархией наследования. Какой способ быстро склонировать эту пачку в наше время?


Раньше народ клепал свои методы на каждый такой класс. Но это надо не забыть исправить такой метод, если исправляется какое-то свойство в клонируемом типе. Да и больше подходит для кастомного копирования с изменениями (т.е. это уже не копирование, а сериализация будет), а не для простого "один к одному".


Потом варианты через сериализаторы - если не сотни мегабайт объектов так клонировать, то пойдёт. Особенно, если не нужно расставлять атрибуты сериализации - т.е. подойдут всякие JSON-сериализаторы.


Ещё с деревьями выражений находил примеры. Но они какие-то замудрёные. Хотя и говорят, что это быстрее всего.


Есть ещё разные фреймворки, но по-моему они внутри делают что-то из вышеперечисленного.


Тут чел собрал разные методы, при этом топит за ручное клонирование. Но то 2012 год. Да и уже говорил, что это скорее для сложного клонирования не "один к одному" и разных не-POCO вещей.


С сериализаторами просто - Serialize, Deserialize, и вот у тебя полная копия POCO. Собственно, сейчас с ними клонирую.

#1 
Программист коренной житель09.10.23 08:03
NEW 09.10.23 08:03 
in Antwort alex445 08.10.23 19:54

В шарпе есть IClonable. Так вот все объекты, которые надо клонировать наследуешь от этого интерфейся и дело в шляпе.


Чтобы не забыть клонировать новые поля можно написать юнит-тест и рефлекшенами считать количество (и названия) полей. Этот прием также можно использовать для тестирования Equal.

#2 
alex445 коренной житель09.10.23 11:21
NEW 09.10.23 11:21 
in Antwort Программист 09.10.23 08:03, Zuletzt geändert 09.10.23 11:24 (alex445)
В шарпе есть IClonable.

Который делает примерно ничего, кроме объявления контракта, что тут что-то клонируется - всё должен делать ты сам, причём ручками. Мне-то надо, чтобы работать поменьше, а результата побольше.


Чтобы не забыть клонировать новые поля можно написать юнит-тест и рефлекшенами считать количество (и названия) полей. Этот прием также можно использовать для тестирования Equal.

Что-то невероятно замудрённое. ))


Короче, продолжаю юзать это - не нужно никаких атрибутов дописывать, интерфейсы реализовывать, и вообще ничего не нужно - тупо на любой POCO класс применяется из коробки:

var temp = JsonConvert.SerializeObject(original);

var copy = JsonConvert.DeserializeObject(temp);

#3 
Срыв покровов gekickt bis 7/7/25 16:05 патриот09.10.23 23:06
NEW 09.10.23 23:06 
in Antwort Программист 09.10.23 08:03
В шарпе есть IClonable. Так вот все объекты, которые надо клонировать наследуешь от этого интерфейся и дело в шляпе.

вот это ты смачно в лужу пукнул.

#4 
Срыв покровов gekickt bis 7/7/25 16:05 патриот09.10.23 23:08
NEW 09.10.23 23:08 
in Antwort alex445 09.10.23 11:21, Zuletzt geändert 09.10.23 23:08 (Срыв покровов)
Короче, продолжаю юзать это - не нужно никаких атрибутов дописывать, интерфейсы реализовывать, и вообще ничего не нужно - тупо на любой POCO класс применяется из коробки:

Как оно с private свойствами работает?

Падает ли, если в дереве объектов окажется циклическая зависимость?

#5 
AlexNek патриот09.10.23 23:34
AlexNek
NEW 09.10.23 23:34 
in Antwort alex445 08.10.23 19:54
alex445 коренной житель10.10.23 00:00
NEW 10.10.23 00:00 
in Antwort Срыв покровов 09.10.23 23:08, Zuletzt geändert 10.10.23 00:05 (alex445)
Как оно с private свойствами работает?
Падает ли, если в дереве объектов окажется циклическая зависимость?

Для приватных надо атрибуты расставлять. Или настройки для всего типа делать (т.е. тоже атрибут).

Цикл можно заигнорить.

Другой объект в составе сериализуемого можно сериализовать со всеми его данными, а можно лишь ссылку на него.

#7 
Срыв покровов gekickt bis 7/7/25 16:05 патриот10.10.23 06:55
NEW 10.10.23 06:55 
in Antwort alex445 10.10.23 00:00

короче нет серебряной пули (((

Плюс у тебя во второй ссылке реестр идёт про библиотеку Newtonsoft

#8 
Программист коренной житель10.10.23 08:27
NEW 10.10.23 08:27 
in Antwort Срыв покровов 09.10.23 23:06
вот это ты смачно в лужу пукнул.

IClonable и был придуман для того, что хочет Алекс.

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

#9 
alex445 коренной житель10.10.23 09:24
NEW 10.10.23 09:24 
in Antwort Срыв покровов 10.10.23 06:55, Zuletzt geändert 10.10.23 09:33 (alex445)
короче нет серебряной пули (((
Плюс у тебя во второй ссылке реестр идёт про библиотеку Newtonsoft

Конечно пулит нет. Я же упростил и говорил про POCO.


Эту либу я и раньше юзал. МСовская была не очень раньше. Они её дорабатывают, но всё равно не дотягивает до json.net. Но суть та же - клонирование несложных объектов через сериализацию до сих пор самый простой способ клонирования, чем вводить свои копировщики, интерфейсы и прочее. Если будете свои копировщики вводить, то столкнётесь с теми же проблемами, что и при сериализации - как ссылки обрабатывать, как циклические зависимости и прочее. И придёте к тем же решениям - вводить нотацию для ссылок или копировать как есть с дублированием данных, опция для игнора циклов, и т.п. Только сами вы это будете неделями и месяцами разрабатывать со всеми сопустствующими ошибками. А сериализаторы уже готовы и отлажены. Проще добить свои классы до сериализации (расставить атрибуты) и воспользоваться ей, чем вводить свои копировщики.


IClonable - старый дотнетовский интерфейс, введённый ещё до появления других удобных инструментов клонирования. Он никак не помогает клонировать, т.к. не содержит никаких вспомогательных инструментов, а просто декларация, широко используемая в самом Дотнете. Написать интерфейс клонирования с одним едиственным методом без реализации - что может быть проще. А вот реализовать... Атавизм, как и класс HashTable и многие другие, оставленные для совместимости.

#10 
alex445 коренной житель10.10.23 09:41
NEW 10.10.23 09:41 
in Antwort Программист 10.10.23 08:27, Zuletzt geändert 10.10.23 09:42 (alex445)
вот это ты смачно в лужу пукнул.
IClonable и был придуман для того, что хочет Алекс.
Никто кроме самого объекта не может знать как надо клонироваться (и надо ли вообще). Так что правильный путь - реализация IClonable. Все остальное - костыли, которые посыпятся в самый неподходящий момент.

С интерфейсом проблема в том, что он декларирует наличие реализации, но никак не отслеживает её актуальность при изменении типа. Тип изменился, и разработчик должен посмотреть, какие другие части этого типа тоже должны измениться. Со сложными типами это море работы, и всё равно можно что-то упустить. Тогда как атрибуты находятся там же, где и изменяющиеся вещи в типе - прямо над ними. Так что забыть изменить сериализацию сложнее - это надо специально атрибуты игнорировать.


Предлагают обложиться тестами, ага. Только как в тестах отслеживать, что там теперь должны быть новые члены? Создавать тестовые объекты через рефлексию, проходя по всем текущим свойствам и сравнивая с объектом после сериализации? Вы тогда тесты будете разрабатывать дольше, чем сами типы, а их сложность может потребовать тесты над тестами. А по заветам отцов-основателей, тесты должны быть максимально простыми.

#11 
MrSanders коренной житель10.10.23 10:40
NEW 10.10.23 10:40 
in Antwort Программист 10.10.23 08:27
Так что правильный путь - реализация IClonable. Все остальное - костыли, которые посыпятся в самый неподходящий момент.

+1. И "копирующий конструктор".

#12 
Murr патриот10.10.23 11:56
Murr
NEW 10.10.23 11:56 
in Antwort MrSanders 10.10.23 10:40

И "копирующий конструктор".

-----

В С++ - да.

В С# - уже нет.


Насколько Я помню среда .НЕТа пользует IClonable по своему усмотрению, а про "копирующий конструктор" она не в курсе.

#13 
Срыв покровов gekickt bis 7/7/25 16:05 патриот10.10.23 13:47
NEW 10.10.23 13:47 
in Antwort Murr 10.10.23 11:56

Кто чем у тебя пользуется?

#14 
alex445 коренной житель10.10.23 16:16
NEW 10.10.23 16:16 
in Antwort Срыв покровов 10.10.23 13:47, Zuletzt geändert 10.10.23 16:20 (alex445)

Посмотрите на количество имеющих этот интерфейс внутренних классов Дотнета (список "Derived") -

https://learn.microsoft.com/en-us/dotnet/api/system.iclone...

Все они юзают его, а не какой-то конструктор-копировщик. Последний - действительно пришёл из плюсов.


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

#15 
Срыв покровов gekickt bis 7/7/25 16:05 патриот10.10.23 17:35
NEW 10.10.23 17:35 
in Antwort alex445 10.10.23 09:24
Конечно пулит нет. Я же упростил и говорил про POCO.

а это все ещё POCO


#16 
alex445 коренной житель10.10.23 23:02
NEW 10.10.23 23:02 
in Antwort Срыв покровов 10.10.23 17:35

И что, не сериализируется?

#17 
Срыв покровов gekickt bis 7/7/25 16:05 патриот11.10.23 12:14
NEW 11.10.23 12:14 
in Antwort alex445 10.10.23 23:02

предположу, что нет.

По WCF точно не передаётся, если присутствует циклическая зависимость.

#18 
alex445 коренной житель11.10.23 13:38
NEW 11.10.23 13:38 
in Antwort Срыв покровов 11.10.23 12:14

Тогда либо игнорить циклы, либо настраивать модели, расставляя атрибуты, чтобы часть полей не сериализовывалась. Но это уже не просто вызвать пару методов для клонирования.

#19 
Срыв покровов gekickt bis 7/7/25 16:05 патриот11.10.23 13:48
NEW 11.10.23 13:48 
in Antwort alex445 11.10.23 13:38

если часть полей не клонируется, то это уже не клонирование.

#20 
Murr патриот11.10.23 15:00
Murr
NEW 11.10.23 15:00 
in Antwort Срыв покровов 11.10.23 13:48

Это вполне нормально.

Те самые ссылочное поля бессмысленно клонировать - их надо инициировать ссылками на объекты.

#21 
alex445 коренной житель11.10.23 15:23
NEW 11.10.23 15:23 
in Antwort Срыв покровов 11.10.23 13:48, Zuletzt geändert 11.10.23 15:26 (alex445)

Я точно не знаю, как эта ситуация обрабатывается. Ссылочные данные могут быть сериализованы ссылками в той либе. Но ссылке в джейсоне не стандартизированы, поэтому каждая либа их делает по-своему.


А циклы - можно не сериализовать. Но непонятно, что это значит - вообще поле пропускается, или на глубину 1 всё же сериализуется. Тестировать надо. По идее, если ссылочные типы просто заменять ссылками, то циклов возникать не должно - мы просто пишем для ссылочного поля его ссылочный айди (в нотации сериализованного джейсона - см. выше мой линк на ту либу), и дальше вглубь это поле не сериализуем. Всё, от цикла избавились.


Но опять же, логика присвоения эти ссылочных айди - чтобы на одну ссылку разные айди не присвоил. Есть ли там сравнение адресов ссылок, или просто каждому следующему ссылочному полю идёт присвоение инкрементированного айди.

#22 
AlexNek патриот11.10.23 17:46
AlexNek
NEW 11.10.23 17:46 
in Antwort alex445 11.10.23 15:23
Вот подумал, а для чего это вообще понадобилось? Отчего мне раньше никогда не требовалось? Может что то не так с хотелками?
#23 
alex445 коренной житель11.10.23 18:16
NEW 11.10.23 18:16 
in Antwort AlexNek 11.10.23 17:46, Zuletzt geändert 11.10.23 18:21 (alex445)

Я хочу сделать у своего проекта in-memory базу данных. Просто таблицы без связей, которые при старте приложения загружаются из текстовых файлов. Таблицы эмулируются банальными словарями, где айдишники это ключи, а значения - объекты. И потом я из этой "базы данных" читаю строки. Когда с обычной базой данных работаешь, ты создаёшь копию такого объекта (строки) у себя в приложении. А в моём варианте, чтобы не копировать ссылку из моих словарей на один и тот же объект, нужна операция клонирования. И т.к. связей между "таблицами" у меня нет, то и циклов у меня нет. В обычных БД, в ORM, вопрос с цикличностью решается ленивой загрузкой, насколько я знаю. Ну и программист сам определяет, насколько глубоко он там связи между таблицами запросит.


Чуваки из игрового мира не привыкли заморачиваться базами данных на клиенте. И в чём-то они правы - возиться ещё с ними. А в Дотнете такой удобный механизм LINQ to objects, да ещё типизированный - очень похожий на LINQ to entities, который используется для БД. И раз уж всё равно объекты сериализовать надо (сохранения, загрузки состояний), то убью двух зайцев одним ударом.

#24 
AlexNek патриот11.10.23 19:32
AlexNek
NEW 11.10.23 19:32 
in Antwort alex445 11.10.23 18:16
А в моём варианте, чтобы не копировать ссылку из моих словарей на один и тот же объект, нужна операция клонирования

всё равно не доходит. Если это всё реадонди, то какая разница?


Я хочу сделать у своего проекта in-memory базу данных.

типа NoSQL? А зачем то самому делать? Пару строк и все работает. https://www.infoworld.com/article/3672154/how-to-use-ef-co...

Ну или типа этого https://www.litedb.org/

#25 
alex445 коренной житель11.10.23 21:18
NEW 11.10.23 21:18 
in Antwort AlexNek 11.10.23 19:32, Zuletzt geändert 11.10.23 21:20 (alex445)

типа NoSQL? А зачем то самому делать? Пару строк и все работает. https://www.infoworld.com/article/3672154/how-to-use-ef-co...
Ну или типа этого https://www.litedb.org/

Я не знаю, что такое nosql, но у меня есть коллеции объектов в памяти, я их сериализую в текстовый человекочитаемый файлик, потом могу подправить его хоть в Блокноте, потом сохранить и десериализовать снова в своё приложение. Если эта штука по последней ссылке так может (особенно человекочитаемый файлик для данных таблицы), то посмотрю её. Но они предлагают какую-то свою Студию юзать для редактирования файлов. Похоже, что файлы таблиц там не человекочитаемые. И редактировать в разных текстовых редакторах я их не смогу.

#26 
AlexNek патриот11.10.23 21:41
AlexNek
NEW 11.10.23 21:41 
in Antwort alex445 11.10.23 21:18
Я не знаю, что такое nosql

не страшно

https://thecode.media/nosql/

https://skillbox.ru/media/code/nosql-chto-eto-za-bazy-dann...

есть коллекции объектов в памяти, я их сериализую в текстовый человекочитаемый файлик

Ломит редакторы делать? Это разве удобно для пользователей?

Но тоже не вижу зачем клонирование...

Делаем словарик <ObjectType, Object>

Делаем цикл по словарю сохраняем каждый в json файл, где ключ имя файла. Для чтения наоборот: цикл по файлам, создаём объекты.

Если не огромная коллекция то можно и всё сразу выгрузить в один файл.

litedb - не для этого

#27 
alex445 коренной житель11.10.23 21:42
NEW 11.10.23 21:42 
in Antwort AlexNek 11.10.23 21:41
Я не знаю, что такое nosql

не страшно

https://thecode.media/nosql/

https://skillbox.ru/media/code/nosql-chto-eto-za-bazy-dann...

Да не, не так. - Я не хочу знать, что такое nosql. )))

#28 
alex445 коренной житель11.10.23 21:44
NEW 11.10.23 21:44 
in Antwort AlexNek 11.10.23 21:41, Zuletzt geändert 11.10.23 21:46 (alex445)
Делаем цикл по словарю сохраняем каждый в json файл, где ключ имя файла. Для чтения наоборот: цикл по файлам, создаём объекты.
Если не огромная коллекция то можно и всё сразу выгрузить в один файл.

Не, мне лучше, чтобы на одну таблицу один файл. Мне проще даже не джейсон, а csv. Типа такого


id,name,race,age

0000,Alex,human,50

0001,Malex,thuman,100500


Но для csv надо будет писать свой сериализатор-десериализатор, а для джейсона уже всё есть.


Редактор пока не нужен. Ну или хватит текстового. Ну или тупо список объектов в датагрид загрузить с возможностью редактирования их свойств через редактирование ячеек датагрида.

#29 
AlexNek патриот11.10.23 21:55
AlexNek
NEW 11.10.23 21:55 
in Antwort alex445 11.10.23 21:44
чтобы на одну таблицу один файл

Что значит одна таблица?


Но для csv надо будет писать свой сериализатор-десериализатор, а для джейсона уже всё есть.

ну вот именно

Делов то на полчаса или и того меньше.

#30 
AlexNek патриот11.10.23 21:57
AlexNek
NEW 11.10.23 21:57 
in Antwort alex445 11.10.23 21:42
Я не хочу знать, что такое nosql.

ну тут сорри, ничем не могу помочь. Каждый решает для себя. Мне бы было некомфортно.

#31 
Murr патриот11.10.23 21:59
Murr
NEW 11.10.23 21:59 
in Antwort alex445 11.10.23 18:16

in-memory базу данных

-----

И чем тебя обычный DataSet не устраивает?


Таблицы эмулируются

-----

Нахрена?

Таблица, со всей функциональностью, уже есть.

Делаешь ее строго типизированной и забываешь об 95% проблем.

#32 
Срыв покровов gekickt bis 7/7/25 16:05 патриот11.10.23 22:33
NEW 11.10.23 22:33 
in Antwort Murr 11.10.23 15:00
Это вполне нормально.
Те самые ссылочное поля бессмысленно клонировать - их надо инициировать ссылками на объекты.

ничего не понял

Как сделать клонирование ссылочных атрибутов объекта?

#33 
alex445 коренной житель12.10.23 22:53
NEW 12.10.23 22:53 
in Antwort Срыв покровов 11.10.23 22:33

Он имеет ввиду, что при клонировании ссылки на объект восстанавливаются как ссылки, а не как объекты со своими полями. Но при сериализации и десериализации ссылки теряются, т.к. уже может не существовать объект, на который ссылаются. Либа по джейсону, что я предлаг, может сохранять ссылки.


Сама идея сериализации - мы вытаскиваем какой-то объект или граф объектов из контекста и разрываем все свази этого графа объектов с контекстом. При десериализации мы восстанавливаем данные графа, но не его связи с контекстом, т.к. обычно уже сам контект либо поменялся, либо уже не существует. Поэтому пре сериализации нет смысла сохранять все ссылки, а только те, что связывают объекты в сериализуемом графе.


Мне надо создать копию объекта без потери контекста, т.к. я генерю её тут же - контекст не успевают измениться. Но я создаю копии лишь данных, а не ссылок. Новую копию я ввожу в контекст, создавая для неё свои ссылки, а не копируя ссылки оригинала. В частности, я генерю для копии новый айдишник. И теперь это копия лишь в смысле данных, но ссылаться на неё нужно уже по своему уникальному айдишнику.

#34 
alex445 коренной житель12.10.23 22:55
NEW 12.10.23 22:55 
in Antwort Murr 11.10.23 21:59

Таблицы эмулируются

-----

Нахрена?

Таблица, со всей функциональностью, уже есть.

Делаешь ее строго типизированной и забываешь об 95% проблем.

Мне надо, чтобы сохранённый вариант был человекочитаемый и достаточно легко редактируемый в текстовом редакторе типа Блокнота. Джейсон не нравится тем, что он некомпактный - даже несколько десятков записей будут требовать много скроллинга для просмотра. А вот формат csv очень даже компактный - можно эти несколько десятков уместить на один экран.

#35 
AlexNek патриот12.10.23 23:05
AlexNek
NEW 12.10.23 23:05 
in Antwort alex445 12.10.23 22:55
А вот формат csv очень даже компактный

а не пробовали его реально редактировать в блокноте? когда пара сотен строк, и пара десятков столбцов с данными разной длины.

#36 
alex445 коренной житель13.10.23 00:52
NEW 13.10.23 00:52 
in Antwort AlexNek 12.10.23 23:05, Zuletzt geändert 13.10.23 00:55 (alex445)

Тогда согласен. Просто я думаю о своём, а у меня данные компактные. Единственное что, в csv вроде только один символ-разделитель можно сделать. И даже если это будет таб, то столбцы всё равно неровными могут быть. Два таба в качестве разделителя вроде нельзя. Хотя, там стандарта на разделитель нет - как считывающий код сделаешь, так и будет... Вобщем, csv - компромиссный вариант. Но он мне в любом случае не нужен, т.к. генерить его надо ручками, а не автоматом натравил либу на класс и она всё сериализировала почти как надо, без расстановки атрибутов и громождения конфигураторов.

#37 
Программист коренной житель13.10.23 07:50
NEW 13.10.23 07:50 
in Antwort alex445 12.10.23 22:55
Мне надо, чтобы сохранённый вариант был человекочитаемый и достаточно легко редактируемый в текстовом редакторе типа Блокнота.

DataSet.WriteXml


А вот формат csv очень даже компактный - можно эти несколько десятков уместить на один экран.

Уместить-то может быть и можно (хотя зависит от длины хранимых данных), а вот найти нужное поле или не дай бог редактировать - это трэш.

#38 
alex445 коренной житель13.10.23 10:41
NEW 13.10.23 10:41 
in Antwort Программист 13.10.23 07:50

Датасет же ин-мемори коллеция данных, а не на диске. Т.е. к нему нужно иметь внешнее хранилище так и так. И ещё нужна отдельная прога для поддержки датасетов и редактирования через них данных - чтобы хотя бы грид с возможностью редактирования показала.


И датасет это не один файл, а кучка (минимум 4, вроде, на которые разваливается .xsd), описывающих один датасет.


У датасета и дататейбл из всей нужной функциональности - поддержка IList, IEnumerable и сериализации в XML. Сериализовать POCO в XML или джейсон можно и так из коробки. И загнать свои данные во что-то IEnumerable или IList я тоже легко могу - банальный словарь. И затем запрашивать их с полной функциональностью sql-подобных запросов LINQ to objects. Тогда зачем мне вся эта инфраструктура датасетов, если поддерживать её сложнее, и редактировать быстро извне в простой сторонней программе типа Блокнота нельзя?

#39 
Программист коренной житель13.10.23 11:41
NEW 13.10.23 11:41 
in Antwort alex445 13.10.23 10:41
Датасет же ин-мемори коллеция данных, а не на диске.

Именно так.


Т.е. к нему нужно иметь внешнее хранилище так и так.

Храни как хочешь. Хоть в XML :)


И ещё нужна отдельная прога для поддержки датасетов и редактирования через них данных - чтобы хотя бы грид с возможностью редактирования показала.

Зачем? :) Если в ТЗ такого нет, то не нужна. А если в ТЗ есть, то ты и со своими классами должен эту прожку написать.


И датасет это не один файл, а кучка (минимум 4, вроде, на которые разваливается .xsd), описывающих один датасет.

WriteXML создает одни XML файл. А ReadXML (внезапно) этот XML читает и воссоздает DataSet.


Тогда зачем мне вся эта инфраструктура датасетов

Если тебе нужна лайт BD, то DataSet оптимальное решение. А еще DataSet можно подружить с твоим любимым EF и вообще кайф :D


редактировать быстро извне в простой сторонней программе типа Блокнота нельзя?

Ну конечно можно. XML вполне себе поддается редактированию блокнотом :) А еще можно там искать данные при помощи XPath и менять только то, что надо :D :D :D

#40 
Murr патриот13.10.23 13:57
Murr
NEW 13.10.23 13:57 
in Antwort alex445 12.10.23 22:55

чтобы сохранённый вариант был человекочитаемый

------

И какие проблемы?

Подставляешь нужный сериалайзер и получаешь что требуется.

#41 
Murr патриот13.10.23 14:03
Murr
NEW 13.10.23 14:03 
in Antwort alex445 13.10.23 00:52

то столбцы всё равно неровными могут быть

-----

Какая хрень...

Дампишь дататабле с дополнением полей до полного и получаешь абсолютно ровненькие колонки...

#42 
Murr патриот13.10.23 14:11
Murr
NEW 13.10.23 14:11 
in Antwort alex445 13.10.23 10:41

из всей нужной функциональности

-----

Из всей известной тебе функциональности.

Там вполне работает LINQ, кроме этого - фильтрация по SQL.


Тогда зачем мне вся эта инфраструктура датасетов, если поддерживать её сложнее, и редактировать быстро извне в простой сторонней программе типа Блокнота нельзя?

-----

Это ограничения твоих знаний...

#43 
alex445 коренной житель13.10.23 14:17
NEW 13.10.23 14:17 
in Antwort Программист 13.10.23 11:41
Если тебе нужна лайт BD, то DataSet оптимальное решение. А еще DataSet можно подружить с твоим любимым EF и вообще кайф :D

Мне нужна ещё более лайт, чем датасеты - без связей между таблицами. Чем пачка List<T> или Dictionaty<T,T> не лайт ин-мемори ДБ без связей? Просто она не relational. И EF не нужен, т.к. всё и так типизировано. ))


Вопрос лишь в том, как хранить на диске. С датасетами - ок, можно писать-читать XML. Но и List<T> можно сериализовать в джейсон без проблем, если там POCO без связей.


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

#44 
alex445 коренной житель13.10.23 14:19
NEW 13.10.23 14:19 
in Antwort alex445 13.10.23 14:17, Zuletzt geändert 13.10.23 14:26 (alex445)

Посмотрел в Unity 3d - вроде, System.Data.dll подключается. Ок, можно оставить на заметку.


Посмотрел best practices в других небольших игровых проектах - не юзают люди БД. Хранят всё в джейсоне или YAML, редко в XML. Потом всё грузят в память и читают из памяти как из хранилища (базы данных). Там сама идея, что зависимости тебе нужны лишь при работающей программе - ты грузишь объекты из БД и создаёшь связи между ними. А в самой БД связи не нужны. А если хочешь состояние со связями сохранить, то сериализуешь, а не в БД сохраняешь. Чтобы в БД сохранить состояние, это надо, чтобы все объекты состояния присутствовали в БД в виде таблиц со связями. А это зачастую не нужно.

#45 
AlexNek патриот13.10.23 17:59
AlexNek
NEW 13.10.23 17:59 
in Antwort alex445 13.10.23 14:17
Вопрос лишь в том, как хранить на диске.


Опять не понимаю. А зачем вообще нужно сохранять на диске да и еще в удобочитаемом виде?

Откуда данные берутся вначале? Отчего нельзя данные редактировать сразу в проге?

#46 
alex445 коренной житель13.10.23 20:38
NEW 13.10.23 20:38 
in Antwort AlexNek 13.10.23 17:59

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


В программе нет редактора данных. Отображение есть, а редактора нет. В теории можно сделать, но там стандартного грида из коробки нет, а эмулировать списком и по отдельности поля редактировать - муторно. В принципе, всё можно сделать, но мне проще не заморачиваться этим, а редактировать извне в каком-нибудь блокноте.

#47 
AlexNek патриот13.10.23 21:37
AlexNek
NEW 13.10.23 21:37 
in Antwort alex445 13.10.23 20:38
Потом вручную буду редактировать джейсон - добавлять новые данные

А в чём разница добавлять или редактировать?


А что то типа этого чем не нравится?

https://github.com/mcw82/propertygrid

#48 
alex445 коренной житель14.10.23 00:58
NEW 14.10.23 00:58 
in Antwort AlexNek 13.10.23 21:37, Zuletzt geändert 14.10.23 01:06 (alex445)

Это для редактора, не для рантайма. Редактор в Юнити - это когда делаешь игру, а рантайм - когда её запускаешь.


Да в принципе много можно чего найти. Бесплатного и платного. При желании на Юнити можно сделать какой угодно датагрид с привязками и прочим. Ну будет сколько-то стоить - можно заплатить. Просто мне это сейчас не надо. Я хочу как можно быстрее и проще избавиться от рутины и делать то, что интересно. Проще добавить пару объектов в коде, сохранить, и потом раз в неделю добавлять ещё в качестве теста по паре объектов в текстовом файле, чем писать самому какой-то сложный контрол для редактирования в игре, или искать подобный уже готовый и его осваивать.


Если захочу не вручную, то можно что-то типа такого использовать.

#49 
AlexNek патриот14.10.23 14:31
AlexNek
NEW 14.10.23 14:31 
in Antwort alex445 14.10.23 00:58

ааа, так для себя и юнити. Конечно, не нужен редактор. Консоль приложение для генерации файла вполне достаточно будет, думаю

#50 
alex445 коренной житель21.10.23 13:41
NEW 21.10.23 13:41 
in Antwort alex445 08.10.23 19:54

Ну и конечно никаких свойств с неопределёнными типами, типа интерфейсов, object и прочего.

#51 
AlexNek патриот21.10.23 17:02
AlexNek
NEW 21.10.23 17:02 
in Antwort alex445 21.10.23 13:41

но вот зачем клонирование нужно, так и не дошло смущ

Генерим всё что надо и усё....

#52 
alex445 коренной житель21.10.23 20:37
NEW 21.10.23 20:37 
in Antwort AlexNek 21.10.23 17:02, Zuletzt geändert 21.10.23 20:50 (alex445)

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


Ещё раз, у меня такая идея. Есть импровизированная база данных в виде файликов (текстовые - джейсоны, 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 это нехилая такая программулина. А без неё будете в командной строке таблицы редактировать и выводить.

#53 
Срыв покровов gekickt bis 7/7/25 16:05 патриот21.10.23 20:59
NEW 21.10.23 20:59 
in Antwort alex445 21.10.23 20:37, Zuletzt geändert 21.10.23 21:02 (Срыв покровов)

говнокод какой-то.
почему не сделать чот типа Repository или Factory, которая при инициализации запоминает, что Warrior‘а нужно создавать со свойствами attack 10, defence 5?

В Repository делаешь функцию T GetInstance<T>() where T : Character и все чистенько без дрочки с серей и десерей.

#54 
alex445 коренной житель21.10.23 21:01
NEW 21.10.23 21:01 
in Antwort Срыв покровов 21.10.23 20:59, Zuletzt geändert 21.10.23 21:13 (alex445)

Я так и делаю - у меня есть класс репы, который всё это и делает. А роль фабрики выполняет клонирующий метод.


Свойства attack 10, defence 5 надо где-то хранить, а ещё редактировать. Не в коде же? Поэтому вот во внешних файликах, эмулирующих таблицы.


Ещё раз, была бы легко доступна нормальная СУБД, я бы её использовал. Но в Юнити всё сделано, чтобы использовали их продукты и привязывались к их движку, а всё постороннее интегрируется сложно. Так все делают, не только они.


В принципе, подошли бы наверное мини СУБД из ссылок АлексНека, если весь движок это одна ДЛЛка. Но они там непонятно в каких файлах хранят данные. А мне нужно простые для редактирования.

#55 
Срыв покровов gekickt bis 7/7/25 16:05 патриот21.10.23 21:04
NEW 21.10.23 21:04 
in Antwort alex445 21.10.23 21:01

нифига ты не так делаешь.
у тебя список эталонных объектов и каждый раз ты берешь один и клонируешь его

#56 
AlexNek патриот21.10.23 21:04
AlexNek
NEW 21.10.23 21:04 
in Antwort alex445 21.10.23 20:37

Ага, в проге есть объекты, которым нужно сохранять состояние и создавать их на основе начальных данных.

И процесс создания объекта назван "клонированием". Ну да ладно, можно и так посмотреть, хотя это должны быть два различных объекта.

И в сериализации когда то будет проблема обновления объекта.

#57 
Срыв покровов gekickt bis 7/7/25 16:05 патриот21.10.23 21:07
NEW 21.10.23 21:07 
in Antwort alex445 21.10.23 21:01

ну и еще если у тебя по условию задачи в файлике один хер каждый тип только один раз записан, то почему его свойства не прописывать в стандартном конструкторе класса?

Ты сначала объяснял по-другому, якобы тебе нужно инициировать много перосонажей одного типа.

#58 
alex445 коренной житель21.10.23 21:19
NEW 21.10.23 21:19 
in Antwort Срыв покровов 21.10.23 20:59
В Repository делаешь функцию T GetInstance<T>() where T : Character и все чистенько без дрочки с серей и десерей.

Ну а сами-то функции что делают? Весь "говнокод" как раз в них и спрятан будет. У меня тоже самое.

#59 
alex445 коренной житель21.10.23 21:20
NEW 21.10.23 21:20 
in Antwort Срыв покровов 21.10.23 21:04, Zuletzt geändert 21.10.23 21:21 (alex445)
нифига ты не так делаешь.
у тебя список эталонных объектов и каждый раз ты берешь один и клонируешь его

А какая разница, как ваш GetInstance создаёт этот инстанс? Хочу и клонирую - я же расписал, как, зачем и почему так делаю. Весь "говнокод" за этим методом спрятан. Будет СУБД, поменяю на СУБД, а метод останется тем же. Всё по фен-шую, но пока без СУБД.

#60 
alex445 коренной житель21.10.23 21:26
NEW 21.10.23 21:26 
in Antwort AlexNek 21.10.23 21:04
И в сериализации когда то будет проблема обновления объекта.

Конечно будет. И её никак не решить по-нормульному, чтобы все острые углы обходить. Обычно обвешивают атрибутами и дата контрактами, которые отсутствующие поля игнорируют,или оставляют, но помечают как устаревшие и неиспользуемые, новые добавленные поля дефолтят, или что-то подобное. Сохранённые состояния нужно тоже обновлять. Или забыть про них и продолжить с новыми. Так поступают везде - настройки программы в БД, сохранения из игр, несовместимые с новыми версиями. Области разные, а подход один.

#61 
alex445 коренной житель21.10.23 21:29
NEW 21.10.23 21:29 
in Antwort Срыв покровов 21.10.23 21:07, Zuletzt geändert 21.10.23 21:30 (alex445)
ну и еще если у тебя по условию задачи в файлике один хер каждый тип только один раз записан, то почему его свойства не прописывать в стандартном конструкторе класса?


Ты сначала объяснял по-другому, якобы тебе нужно инициировать много перосонажей одного типа.

Так и осталось - нужно инициировать много персонажей одного типа. Для этого клонирую их из списка, созданного из файлика.


В конструкторе хардкодить - это в исходниках значит. А мне нужны внешние конфигурационные файлы, чтобы легко можно было в блокнотике подправить.


Во! Назову это конфигурационными файлами. А то что-то я сущности придумываю новые, а по сути это конфиги и есть.

#62 
alex445 коренной житель21.10.23 22:51
NEW 21.10.23 22:51 
in Antwort Срыв покровов 21.10.23 20:59, Zuletzt geändert 21.10.23 22:54 (alex445)
В Repository делаешь функцию T GetInstance() where T : Character и все чистенько без дрочки с серей и десерей.

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


А сериализация для клонирования используется ещё и потому, что я её же использую и для сохранения состояния объектов. Если бы без этого, то можно было бы просто написать свой конструктор-копировщик. А так - немного меньше работы.

#63 
Срыв покровов gekickt bis 7/7/25 16:05 патриот21.10.23 23:28
NEW 21.10.23 23:28 
in Antwort alex445 21.10.23 21:29
Так и осталось - нужно инициировать много персонажей одного типа. Для этого клонирую их из списка, созданного из файлика.

тогда почему в твоём файле не прописать сразу всех персонажей?

Типа

Вася - Воин - меткость 100

Петя - воин - меткость 200

#64 
alex445 коренной житель22.10.23 10:06
NEW 22.10.23 10:06 
in Antwort Срыв покровов 21.10.23 23:28, Zuletzt geändert 22.10.23 10:18 (alex445)

Кроме персонажей, которые существуют в единственном экземпляре, есть ещё враги, которых может быть много с одними и теми же параметрами. А кроме того, надо бы подумать, как и персонажей тоже клонировать. Скажем, есть базовый типа "воин", и игрок может создать себе отряд из них, но разбросать им дополнительные характеристики к базовым. Это типичная возможность таких игр с незапамятных времён. Ну и ещё неплохо бы предусмотреть возможность клонировать любого текущего персонажа или врага - на какую-нибудь будущую игровую механику с клонированием. А если заложить возможность сериализации для сохранения состояния, то клонирование получается дополнительно почти автоматически. Нужно только добавить генерацию уникального айдишника. Мой метод клонирования так и делает.


существуют в единственном экземпляре, есть ещё враги, которых может быть много с одними и теми же параметрами

Имеется ввиду класс врагов. Скажем, есть "воин", есть "крутой воин", и у них разные базовые характеристики. Но я могу пойти ещё дальше, и добавить при создании врага из базовых характеристик небольшой разброс, чтобы клоны немного отличались. Это такая особенная игровая механика. Не так уж много где это реализовано - насколько я знаю, японцы вообще этим не заморачиваются. А у меня будет особенность. Ну и по-любому это всё надо добавлять в коллекцию уже созданных и существующих в игровом мире врагов с уникальными идентификаторами. Вот поэтому механика клонирования и нужна.


Ещё раз, если сохранение состояния через сериализацию даёт возможность сохранять весь граф объектов с уникальными параметрами, то не грех воспользоваться этим для клонирования, чтобы не писать отдельные клонирующие методы, которые будут делать то же, что и сериализатор. Мой клонирующий метод будет просто сериализовать-десериализовать любой уже существующий объект с помощью любого готового фреймворка (в данный момент это Json.NET), и добавлять уникальный айдишник.


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


Вы когда-нибудь делали программу с сохранением состояния? Не просто конфиги или ненадолго один объект в сессии сохранить, а скажем все открытые окна, вкладки и значения в них? Или скажем открыл окно, вбил значения, закрыл, а вбитые значения не стёрлись, а запомнились, и при открытии такого окна снова, то же самое было бы на тех же местах? И чтобы можно было в любой момент засейвить состояние приложения и загрузить потом тоже в любой момент? Это сложнее, чем просто сохранять некоторые настройки в конфигах. Это надо изначально классы объектов и окон писать с возможностью сохранения состояния. Вот Студию вы закрыли, открыли, а все вкладки, инструменты и даже позиция курсора запомнились и открылись так, как были.


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


Ну а если спроектировано всё с сохранением состояния, то клонирование через сериализацию получается почти из коробки.

#65 
Срыв покровов gekickt bis 7/7/25 16:05 патриот22.10.23 18:46
NEW 22.10.23 18:46 
in Antwort alex445 22.10.23 10:06

ну ок, если все равно сериализация нужна, то ладно - можно так сделать.

#66 
Murr патриот22.10.23 20:06
Murr
NEW 22.10.23 20:06 
in Antwort alex445 22.10.23 10:06

и даже позиция курсора запомнились и открылись так, как были

-----

Угу... вот только пока Студия была в оффлайне кто шаловливыми пальчиками поковырялся в редактировавшихся файликах...

Дальше пояснять?

#67 
alex445 коренной житель23.10.23 09:43
NEW 23.10.23 09:43 
in Antwort Murr 22.10.23 20:06

Тогда дефолтная позиция. Также, как и при десериализации, когда кто-то поправил руками джейсон там или исходный класс, и теперь между ними несоответствие.


Большие многомиллиардные корпорации забивают на все эти мелкие проблемы маленьких людей, и если что не соответствует, то говорят просто обновиться и забить на старьё - старые сейвы в новых обновлениях могут не работать, старые конфиги в новых версиях ПО также, и т.д. А вы тут, мелкие сошки, с доходом на всю контору как пара зарплат менеджеров ФААНГов, стараетесь все-все нюансы учесть, вплоть до "а если метеорит упадёт, тогда что? в вашем ПО есть защита от этого?". Проще надо быть. )))

#68 
1 2 3 4 alle