Как стать автором
Обновить
54
-3

Пользователь

Отправить сообщение
Уточнение:
public Point GetPoint() => ...

По моей логике в выражении
if (GetPoint() is Point p) ...

можно применить вывод типа и получить
if (GetPoint() is var p) ...

Нона самом деле это не эквивалентная замена в C# 7.
Лично для меня на интуитивном уровне это что-то вроде деконструкции метода 'Is' со следующей имплементацией (для декларации переменных в самом выражении):

public static bool Is<T>(this T o, out T x) => (x = o) != null; /* or same '(x = o) is T' */

if (GetPoint().Is(out var p) Console.WriteLine("o is Any Type");
else Console.WriteLine("There is not point.");

if (GetPoint() is var p) ...


Но, увы, архитекторы языка посчитали иначе.
В текущей реализации по умолчанию библиотека использует класс Activator и конструктор без параметров для создания новых копий объектов, однако есть возможность переопределять это поведение в профилях репликации, в том числе использовать параметризированные конструкторы для конкретных типов (например, так сейчас работают Regex и StringBuilder).

Вообще стандартные Data Contract сериализаторы от Майкрасофт используют метод FormatterServices.GetUninitializedObject(Type), который создаёт непроинициализированный объект и вовсе не вызывает конструктор, но этот метод недоступен в публичном API для портабельных сборок, только в полном dot net.

Если заинтересуетесь деталями реализации, то спрашивайте, можете также отправить запрос на адрес makeman@tut.by для получения ознакомительного доступа к коду проекта. :)
Хорошо :) расскажу подробнее про историю создания.

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

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

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

2) В какой-то момент начали то и дело всплывать ограничения, накладываемые многими сериализатооами на графы, например, далеко не все могут обработать корректно множественные и циклические ссылки, иногда искажают информацию о типах, каким-то не достаёт гибкости. Захотелось написать такой сериализатор, который съест практически любой граф и восстановит его без искажений, идентично оригиналу, при этом позволит настраивать весь процесс…

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

Другими словами, в обычных сериализаторах логика получения состояния объекта и записи его в файл очень связная, иногда зависимая от формата сериализации, а в RF состояние — это отдельная абстракциях, с которой можно заметно эффективнее работать.

Примерно так и возник фреймворк. Если появятся ещё вопросы, то буду рад ответить! ;)

Ну, если даже взглянуть на пример из статьи, то в нём редактирование сущности в диалоговом окне можно осуществить двумя способами (флаг Create copy): с созданием новой копии объекта и без его копирования с непосредственным редактированием оригинала. Так вот, если изменить оригинал, то правки сразу отразятся в главном окне, но если нажать Cancel, то при скрытии диалога произойдёт откат (реконструкция) до состояния, зафиксированного на снимке.

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

Данная функциональность позволяет вести хронологию изменений графа и сопоставлять снимки в произвольных комбинациях. Например, делать что-то вроде бэкапов или кнопки Undo. Что касается вопроса про граф из 1000 объектов, поясню. Пусть дан такой граф, в какой-то момент делаем его снимок и явным образом помещаем исходные объекты в ReplicationCache (он удерживает экземпляры от сборки мусора и хранит ссылку-идентификатор каждого экземпляра). Далее с графом может происходить что угодно: изменяться структура, добавляться и удаляться объекты, — но когда у нашего снимка будет вызван метод ReconstructGraph, то все объекты из ReplicationCache вернутся к состоянию на момент создания снимка и образуют исходный граф из тысячи объектов.

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

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

RF позволяет довольно гибко сопоставлять снимки объектов, и не только по всем полям. Результатом операции сопоставления является IEnumerable, что позволяет реализовывать даже весьма экзотические сценарии (прекращать сравнение при достижении n различий, пропускать ненужное, вычислять «похожесть» снимков, например, количество одинаковых полей делить на общее, сопоставлять объекты разных типов). Конечно, если логика сравнения бизнес-сущностей в определённом случае очень специфическая, то, возможно, имеет смысл реализовать её вручную, но ведь это тоже не самый распространённый сценарий.

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

Задачу, поставленную в публикации, библиотека решает успешно с приемлемой для данного случая производительностью и минимумом строк кода, поэтому подход вполне практичен.
По INotifyPropertyChanged, пускай изначальное значение свойства было abc, потом пользователь отредактировал его на abcd, после чего на другое значение и так n раз, но в конечном счёте решил вернуть abc и нажал Ok. В итоге нотификация сработала, но значение осталось прежним, не изменилось. Конечно, можно запоминать исходные данные и потом выполнять сравнение, но это же тот самый подход от которого мы хотим отойти…

Да, можно использовать AutoMapper, различные сериализаторы, библиотеку Compare net objects, но Replication Framework, если разобраться детально, во многих отношениях стремится обобщить их функциональность и решать более широкий круг проблем.

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

Или всё-таки что-то показалось не достаточно понятным?
Под подтверждением подразумевается нажатие кнопки Ок.

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

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

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

В принципе, её можно делать и сейчас, но с некоторыми ограничениями. Для стабильных графов она будет работать очевидным образом, то есть примитивные свойства объектов (типов string, int, double, DateTime, etc.) примут значения с любого снимка.

Нюансы появятся с мутирующими графами, когда объекты удаляются и вставляются в граф. Как упоминалось в публикации, для выполнения реконструкции необходимо наличие кэша Dictionary[object, int], который содержит сами объекты, подвергающиеся реконструкции, и их уникальные идентификаторы. Если вдруг объекта не оказывается в кэше, то репликатор создаёт новый экземпляр и добавляет его в кэш (id генерируется инкрементом). Так вот, если на снимках не будет конфликтов в идентификаторах, то «реконструкция наоборот» отработает успешно, в противном случае может возникнуть исключение о несоответствии типов, или же нарушиться структура графа.

Это напоминает мерж коммитов из рабочей ветки в основную, если изменения фиксировать и накатывать последовательно, то всё мержится автоматом, но если применять коммиты вразнобой, то начинаются конфликты. Приблизительно та же ситуация и со снимками в RF.

В библиотеке изначально разделена логика трансляции снимка (ReplicationProfile) и его сохранения (KeepProfile), то есть в дальнейшем без больших затруднений можно добавить сериализацию в бинарный формат, просто на данном этапе разработки было решено ограничиться json, как наиболее человекоориентированным.

Спасибо за интерес и пожелания!
Также, как упоминалось в публикации, результатом выполнения операции сопоставления в RF является IEnumerable, что позволяет использовать Linq для динамического анализа результатов сравнения и значимо для производительности.

В Compare-Net-Objects можно задать пороговое количество различий, когда сравнение прекращается, после чего все они будут занесены в коллекцию public List[Difference] Differences класса ComparisionResult. Но подход с IEnumerable более гибкий, поскольку можно учитывать не только количество различий, но и другие факторы, например, их качество (насколько различие является существенным).
Да, существует такая альтернативная библиотека для сравнения, но Replication Framework предлагает более обобщённый подход…

Например, необходимо проследить изменения в процессе жизни одного экземпляра объекта [графа]. Не всегда есть возможность сделать его точную копию в начальный момент времени, то есть у нас экземпляр только один! Чтобы воспользоваться Compare-Net-Objects необходимо на вход подать два различных экземпляра объекта, чтобы производить между ними сравнение (если же мы подадим один и тот же экземпляр, то в итоге получим равенство). Replication Framework для сравнения нуждается лишь в снимках объектов, а их можно делать в любые моменты времени без ограничений, что даёт возможность организации трекинга состояния, поскольку снимки одного и того же объекта в различные моменты времени вполне могут отличаться.
Использование DTO — это лишь один из частных случаев в разработке.
Хотелось бы акцентировать внимание на том, что помимо сериализации библиотека предназначена для более широкого круга задач.

К примеру, имеется два DTO объекта, и их нужно сравнить. Можно, конечно, написать свою логику сравнения, но проще и быстрее сделать сопоставление снимков, как показано в статье.
Такой вышколенный текст — Вам бы редактором журнала работать =)

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

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

Спасибо за труд и литературность материала!
Не спорю, здесь есть к чему придраться за нарушение «правильной» идеологии :)

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

Представленный в статье «трюк» позволяет обойти некоторые ограничения при выполнении привязок. Безусловно, когда мы получаем больше свободы стоит помнить о безопасности, однако и возможности наши возратают.
Согласен, что такие ситуации скорее исключение из правил, но в то же время они очень «неудобные».

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

Если мы будем делать дополнительное свойство Title во вью-модели, то она будет загрязняться интерфейсным кодом, а также придётся использовать что-то вроде MultiBinding, который, кстати, не на всех платформах поддерживается, плюс создавать IMultiValueConverter. Всё довольно усложняется несмотря на тривиальность задачи. С Inline Converter выглядит просто и понятно.

Также может возникнуть и другая ситуация: основная привязка свойства Text должна идти к свойству Number во вью-модели, но, например, в зависимости от нескольких других различных значений свойств этой вью модели, сама строка должна декорироваться тем или иным образом. Поскольку в обычном конвертере у нас нет доступа к самой вью-модели, приходится искать обходные пути, не всегда красивые. В нашем же случае есть прямой доступ к контесту данных представления, нужной нам вью-модели и всем её свойствам.
Думал над вашим вопросом, и пришёл к выводу, что, возможно, не совсем вас понял.
Вы имеете в виду удаление элементов из строки во время работы XAML-дизайнера среды разработки?

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

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность