Комментарии 21
Присмотритесь так же к проекту Clay от создателей Orchard CMS. Там то же все элегантно сделано. Вот ссылки с документацией
weblogs.asp.net/bleroy/archive/2010/08/16/clay-malleable-c-dynamic-objects-part-1-why-we-need-it.aspx
weblogs.asp.net/bleroy/archive/2010/08/18/clay-malleable-c-dynamic-objects-part-2.aspx
weblogs.asp.net/bleroy/archive/2010/08/16/clay-malleable-c-dynamic-objects-part-1-why-we-need-it.aspx
weblogs.asp.net/bleroy/archive/2010/08/18/clay-malleable-c-dynamic-objects-part-2.aspx
+1
Ага, спасибо за ссылку. Но Clay позволяет делать вот так
IPerson lou = people[0];
за счет использования DynamicProxy от Castle, а он может перехватывать только вызовы виртуальных методов либо методов интерфейса. Т.е. если у меня есть свойство Name и я хочу во вью-модели при сеттере добавить добавить какую-то валидацию на него(которая возможно мне совершенно не нужна или даже вредна в обычной дата-модели), то мне придется сделать свойство виртуальным. Это, конечно, не очень страшно, но мне было интересно сделать решение, которое позволяет не модифицировать сами модельные классы. +1
ээээх, афтор, вам бы все пять способов сделать и замерить быстродействие! цены бы этой статье не было.
но в целом спасибо!
но в целом спасибо!
+2
Честно говоря, основная причина написания статьи — продемонстрировать возможности dynamic type. Но если интересно, то быстродействие я мерил — dynamic выигрывает на создании, ибо кастоловский прокси компилирует объект на лету с emit-апи, но зато потом в моей реализации идут проигрыши на вызове методов реального объекта, поскольку я дергаю их через рефлексию. Впрочем в дата-объекте особо методов быть не должно. Вызов свойств по времени вполне сравним, ибо там я прикрутил вызов через компилируемые лямбда-функции. Что касается решений типа T4 или посташарпа, то там конечно все быстрее, т.к. все нужные действия происходят на компиляции, но зато удобство написания явно ниже(особенно это касается Т4).
0
sdramare, я бы рекомендовал вам не использовать данный подход в продакшене. Сам часто грешу, используя именно динамики для этой задачи, хоть и в более простой форме чем вы.
Но! Для большо́го количества моделей, с больши́ми уровнями вложенности — заметно тормозит. Лучше сделать свой extension для быстрой генерации ViewModel или взять чужой и переделать под себя. Для себя избрал именно такой способ. :)
Но! Для большо́го количества моделей, с больши́ми уровнями вложенности — заметно тормозит. Лучше сделать свой extension для быстрой генерации ViewModel или взять чужой и переделать под себя. Для себя избрал именно такой способ. :)
0
Спасибо за ссылку =) Насчет тормозов — я использовал этот подход для построение визуального редактора абстрактного синтаксического дерева некого упрощающего диалекта питона(в нем описывались формулы преобразования одного значения в другое). При общем количестве нод в графе порядка 50 никаких тормозов не видел. Впрочем ничего не берусь утверждать, все зависит от задачи и проекта.
+1
Интересная задача. Даже дважды перечитал формулировку. :-)
Я обычно для прототипов не сложных редакторов или для мелких утилит использую — там скорость работы действительно будет неотличима на глаз от обычного подхода. А вот для большого вложенного N-арного дерева (я пробовал подключить на работающий проект с «рукописными» VM), где каждая вершина с кучей вложенных моделей, тормоза были заметны невооруженным взглядом. Возможно, там хреновая архитектура для динамиков, но, для себя я выводы сделал.
Я обычно для прототипов не сложных редакторов или для мелких утилит использую — там скорость работы действительно будет неотличима на глаз от обычного подхода. А вот для большого вложенного N-арного дерева (я пробовал подключить на работающий проект с «рукописными» VM), где каждая вершина с кучей вложенных моделей, тормоза были заметны невооруженным взглядом. Возможно, там хреновая архитектура для динамиков, но, для себя я выводы сделал.
0
добавь к сеттору файринг нотификации
Капец :) Это ж вроде не перевод…
0
Да, давайте так.
0
И знаете, в чем крупный и отвратительный недостаток этого подхода? В том, что ваш код перестал быть типизированным. Любые ошибки и опечатки вы отловите уже после компиляции.
При этом вы зачем-то зациклились на прокси, хотя эти задачи прекрасно решаются с помощью обычных объектов и маппинга. А отсюда вырастает целый класс решений по этому поводу, начиная с AutoMapper.
При этом вы зачем-то зациклились на прокси, хотя эти задачи прекрасно решаются с помощью обычных объектов и маппинга. А отсюда вырастает целый класс решений по этому поводу, начиная с AutoMapper.
0
И знаете, в чем крупный и отвратительный недостаток этого подхода? В том, что ваш код перестал быть типизированным. Любые ошибки и опечатки вы отловите уже после компиляции.
Ну во-первых да, знаю, что он перестанет быть типизированным(думаю многие об этом начали догадываться еще прочитав слово dynamic в заголовке). Во-вторых то, что это недостаток, еще очень спорный вопрос. Динамическая типизация имеет как свои плюсы, так и минусы. Посмотрите на тот же питон — на нем прекрасно пишутся приложения, хотя типизация самая что не наесть динамическая. Ну или обжект-си в плане посылки сообщений. Да, ошибки отлавливаются уже после компиляции и по-этому на динамических языках стараются писать через TDD. Впрочем я лично считаю, что и на статике это будет не лишним. Что же касается данного примера, то обратите внимает, что речь идет о довольно простых объект, которые не должны содержать сложную логику, которая сломается. К тому же тем не менее типизацию в каком виде я сохраняю — например если свойство было задано как int, записать в него строку уже не получится.
При этом вы зачем-то зациклились на прокси
Была поставлена задача и предложено решение на основе прокси. Сама задача была взята как база для рассмотрения вопроса о том, как можно использовать новый тип, появившийся в спецификации C# 4.0
эти задачи прекрасно решаются с помощью обычных объектов и маппинга. А отсюда вырастает целый класс решений по этому поводу, начиная с AutoMapper.
Прекрасно если это так, с удовольствием прочитаю вашу статью об этом. Не забудьте об по ставленом в задаче условии автоматического добавления новых свойств для объектов, возвращаемых методами и функциям.
P.S. Ваш комментарий, на мой взгляд, чрезмерно эмоциональный и несет в себе конфликт-агенты. Мне кажется не стоит продолжать дискуссию в таком тоне, вы не находите?
0
«К тому же тем не менее типизацию в каком виде я сохраняю — например если свойство было задано как int, записать в него строку уже не получится.»
С точки зрения компилятора — получится.
«Сама задача была взята как база для рассмотрения вопроса о том, как можно использовать новый тип, появившийся в спецификации C# 4.0»
А это как раз типическая ошибка подхода: «у нас есть новая возможность, как ее использовать». В то время как правильнее исходить из «у нас есть такая задача, как ее решить».
«Прекрасно если это так, с удовольствием прочитаю вашу статью об этом.»
habrahabr.ru/search/?q=automapper
«И в вдогонку — когда вы начинаете использовать дататемплейты, стили, триггеры и байдинги в WPF, то очень часто проверка ошибок на этапе компиляции превращается в пыль»
Мир не ограничивается WPF, вот в чем дело. MVVM используется не только там, и задача создания моделей возникает не только там. В вашей статье WPF возникает только в конце, а до этого вы говорите об абстрактном MVVM.
С точки зрения компилятора — получится.
«Сама задача была взята как база для рассмотрения вопроса о том, как можно использовать новый тип, появившийся в спецификации C# 4.0»
А это как раз типическая ошибка подхода: «у нас есть новая возможность, как ее использовать». В то время как правильнее исходить из «у нас есть такая задача, как ее решить».
«Прекрасно если это так, с удовольствием прочитаю вашу статью об этом.»
habrahabr.ru/search/?q=automapper
«И в вдогонку — когда вы начинаете использовать дататемплейты, стили, триггеры и байдинги в WPF, то очень часто проверка ошибок на этапе компиляции превращается в пыль»
Мир не ограничивается WPF, вот в чем дело. MVVM используется не только там, и задача создания моделей возникает не только там. В вашей статье WPF возникает только в конце, а до этого вы говорите об абстрактном MVVM.
0
С точки зрения компилятора — получится.
Безусловно. Но в данном случае гарантируется целостность данных — если произойдет некорректный сет вы об этом сразу узнаете.
А это как раз типическая ошибка подхода: «у нас есть новая возможность, как ее использовать». В то время как правильнее исходить из «у нас есть такая задача, как ее решить».
Как известно одна и даже задача, сформулированная в общих терминах имеет множество решений. Выбор нужного делается на основе тех или иных критериев оптимальности. Я уже говорил ранее, динамическая типизация имеет свои плюсы и соответственно предпочтительна для решения определенных классов задач. Вы же почему-то отбрасываете ее в принципе, исходя из тезиса «не компилируется — значит плохо». Вас не смущается, что множество веб-решений, комплексная сложность которых вполне сравнима(а то и превосходит) со сложностью десктоп приложений, делаются на языках с динамической типизацией(причем даже менее строгой, чем та, которой привел я в своей статье)?
habrahabr.ru/search/?q=automapper
Вы сами ходили по этой ссылке? Если да, то в какой из этих статей решается задача «автоматического добавления новых свойств для объектов, возвращаемых методами и функциям»? Мне лично кажется, что не в какой, ибо автомаппер в принципе не предназначен для этого. Кстати говоря, что будет, если в одном из классов, который мы маппим, я поменяю имя свойства? А ничего не будет, все прекрасно скомпилируется. Ошибка будет уже на рантайме.
Мир не ограничивается WPF, вот в чем дело. MVVM используется не только там, и задача создания моделей возникает не только там. В вашей статье WPF возникает только в конце, а до этого вы говорите об абстрактном MVVM.Я вам просто пример привел. Не нравится WPF — возьмите Silverlight. А в связке (html+css+js), на которой строятся современные веб-интерфейсы, статической типизацией и не пахло. И ничего, никто не умирает.
Я понимаю, если бы вы мне указали на то, что на текущий момент dynamic плохо поддерживается статическими анализаторами кода. Это да, недостаток. А статика сейчас все больше сдает позиции.
0
«Безусловно. Но в данном случае гарантируется целостность данных — если произойдет некорректный сет вы об этом сразу узнаете.»
Не сразу, а на этапе выполнения.
«вы же почему-то отбрасываете ее в принципе, исходя из тезиса «не компилируется — значит плохо».»
Я ее не отбрасываю. Я просто при прочих равных предпочитаю решения, которые компилируются (пусть даже они требуют, скажем, кодогенератора).
«Кстати говоря, что будет, если в одном из классов, который мы маппим, я поменяю имя свойства?»
… зато если вы поменяете это свойство в модели или ошибетесь в обращении к нему, все сломается на этапе компиляции.
«Не нравится WPF — возьмите Silverlight.»
… и замлом мир тоже не ограничивается, в общем-то.
«на текущий момент dynamic плохо поддерживается статическими анализаторами кода»
Проблема в том, что его вообще очень сложно поддержать анализаторами кода, потому что public void Process(dynamic smth) может быть вызван откуда угодно, и в динамике может быть что угодно. Прощай, анализ. Именно поэтому я предпочту кодогенерацию или типизованный прокси (хоть на рефлекшне) динамику.
«А в связке (html+css+js), на которой строятся современные веб-интерфейсы, статической типизацией и не пахло.»
И знаете, как это усложняет разработку? По сравнению с нормальным типизованным asp.net mvc?
Не сразу, а на этапе выполнения.
«вы же почему-то отбрасываете ее в принципе, исходя из тезиса «не компилируется — значит плохо».»
Я ее не отбрасываю. Я просто при прочих равных предпочитаю решения, которые компилируются (пусть даже они требуют, скажем, кодогенератора).
«Кстати говоря, что будет, если в одном из классов, который мы маппим, я поменяю имя свойства?»
… зато если вы поменяете это свойство в модели или ошибетесь в обращении к нему, все сломается на этапе компиляции.
«Не нравится WPF — возьмите Silverlight.»
… и замлом мир тоже не ограничивается, в общем-то.
«на текущий момент dynamic плохо поддерживается статическими анализаторами кода»
Проблема в том, что его вообще очень сложно поддержать анализаторами кода, потому что public void Process(dynamic smth) может быть вызван откуда угодно, и в динамике может быть что угодно. Прощай, анализ. Именно поэтому я предпочту кодогенерацию или типизованный прокси (хоть на рефлекшне) динамику.
«А в связке (html+css+js), на которой строятся современные веб-интерфейсы, статической типизацией и не пахло.»
И знаете, как это усложняет разработку? По сравнению с нормальным типизованным asp.net mvc?
0
И в вдогонку — когда вы начинаете использовать дататемплейты, стили, триггеры и байдинги в WPF, то очень часто проверка ошибок на этапе компиляции превращается в пыль. Вы никогда не сталкивались с ситуацией, когда байдинг на какой-нибудь прекрасно скомпилированной странице/контроле не работает, т.к. не правильно заданно имя свойства или в датаконтексте по воли злого рока оказался объект не того типа, которого вы ожидали? Является наличие таких особенностей при построении UI через хaml и WPF поводом отказаться от их использования?
0
Статическая типизация это вообще спорная вещь, сама по себе много не дает, зато ее отсутствие подталкивает к написанию модульных тестов, TDD и т.п. Тем более, вы эту типизацию затем все равно теряете в xaml.
Насколько я понимаю, автор как раз хотел избежать рутинной работы по созданию таких объектов, большинство которых скорее всего ничем 'интеллектуальным' заниматься не будут, кроме как оборачивать и пробрасывать вызовы, что по сути и есть проксирование.
Ваш подход мне совсем непонятен. Зачем создавать кучу view models, которые еще и никак не будут связаны с моделью, кроме как логически, да еще и маппинги прописывать? И да, я бы тогда уж с EmitMapper начинал.
На мой взгляд вариантом тут может являться применение шаблона для генерации делегирующего свойства с оповещением нотификации, если говорить о INotifyPropertyChanged. Если говорить о добавочных свойствах, то я думаю, что свойства в студии генерируются проще, чем при подходе автора. А вот с оборачиванием внутренних объектов во view models придется возиться.
Насколько я понимаю, автор как раз хотел избежать рутинной работы по созданию таких объектов, большинство которых скорее всего ничем 'интеллектуальным' заниматься не будут, кроме как оборачивать и пробрасывать вызовы, что по сути и есть проксирование.
Ваш подход мне совсем непонятен. Зачем создавать кучу view models, которые еще и никак не будут связаны с моделью, кроме как логически, да еще и маппинги прописывать? И да, я бы тогда уж с EmitMapper начинал.
На мой взгляд вариантом тут может являться применение шаблона для генерации делегирующего свойства с оповещением нотификации, если говорить о INotifyPropertyChanged. Если говорить о добавочных свойствах, то я думаю, что свойства в студии генерируются проще, чем при подходе автора. А вот с оборачиванием внутренних объектов во view models придется возиться.
0
«Статическая типизация это вообще спорная вещь»
Для кого как.
«ее отсутствие подталкивает к написанию модульных тестов, TDD и т.п.»
А ее наличие — к выявлению части ошибок еще на этапе компиляции, экономя мне минуты при сборке.
«Тем более, вы эту типизацию затем все равно теряете в xaml.»
Замлом жизнь MVVM не ограничивается.
«Зачем создавать кучу view models, которые еще и никак не будут связаны с моделью, кроме как логически, да еще и маппинги прописывать?»
Чтобы получить чистую view model, не дающую побочных эффектов на бизнес-объект, ее породивший.
Для кого как.
«ее отсутствие подталкивает к написанию модульных тестов, TDD и т.п.»
А ее наличие — к выявлению части ошибок еще на этапе компиляции, экономя мне минуты при сборке.
«Тем более, вы эту типизацию затем все равно теряете в xaml.»
Замлом жизнь MVVM не ограничивается.
«Зачем создавать кучу view models, которые еще и никак не будут связаны с моделью, кроме как логически, да еще и маппинги прописывать?»
Чтобы получить чистую view model, не дающую побочных эффектов на бизнес-объект, ее породивший.
0
«Для кого как.»
На мой взгляд существенным преимуществом статической является упрощение статического анализа, иначе с этим возникают серьезные проблемы. А так по опыту могу сказать, что у многих статическая создает ложное чувство корректности программы, в то время как контролирует только часть ошибок, и то глупых. Впрочем на эту тему уже столько сказано и написано, что статическая типизация это все же безусловно спорная вещь.
«А ее наличие — к выявлению части ошибок еще на этапе компиляции, экономя мне минуты при сборке.»
Либо вы работаете над гигантскими проектами и я вам завидую, либо существует проблема запутанности зависимостей, возможно архитектуры. Опять же по опыту могу сказать, что программисты как правило нажимают в студии кнопочку Build и компилятор начинает молотить до победного конца. Если подходить по уму, то в случае с модульными тестами у вас возникнает 'пролема' создания более продуманной структуры зависимостей, что в конечном счете позволяет минимизировать выигрышь обрывания процесса компиляции при обнаружении первой ошибки.
«Тем более, вы эту типизацию затем все равно теряете в xaml.»
Ок, может быть вы его прикрутили куда-то еще. Но MVVM в первую очередь относится к Microsoft, WPF, Silverlight, XAML.
«Чтобы получить чистую view model, не дающую побочных эффектов на бизнес-объект, ее породивший.»
Вы имели в виду объект, который оборачивает View model, а не порождает?
По паттерну view model никак не может аффектить бизнес-объекты. Зато по тому же паттерну View model занимается связыванием model и view, по сути он содержит в себе бизнес-объекты и обеспечивает работу с ними. Подмена модели другими объектами, не связанными с моделью выглядит странно.
На мой взгляд существенным преимуществом статической является упрощение статического анализа, иначе с этим возникают серьезные проблемы. А так по опыту могу сказать, что у многих статическая создает ложное чувство корректности программы, в то время как контролирует только часть ошибок, и то глупых. Впрочем на эту тему уже столько сказано и написано, что статическая типизация это все же безусловно спорная вещь.
«А ее наличие — к выявлению части ошибок еще на этапе компиляции, экономя мне минуты при сборке.»
Либо вы работаете над гигантскими проектами и я вам завидую, либо существует проблема запутанности зависимостей, возможно архитектуры. Опять же по опыту могу сказать, что программисты как правило нажимают в студии кнопочку Build и компилятор начинает молотить до победного конца. Если подходить по уму, то в случае с модульными тестами у вас возникнает 'пролема' создания более продуманной структуры зависимостей, что в конечном счете позволяет минимизировать выигрышь обрывания процесса компиляции при обнаружении первой ошибки.
«Тем более, вы эту типизацию затем все равно теряете в xaml.»
Ок, может быть вы его прикрутили куда-то еще. Но MVVM в первую очередь относится к Microsoft, WPF, Silverlight, XAML.
«Чтобы получить чистую view model, не дающую побочных эффектов на бизнес-объект, ее породивший.»
Вы имели в виду объект, который оборачивает View model, а не порождает?
По паттерну view model никак не может аффектить бизнес-объекты. Зато по тому же паттерну View model занимается связыванием model и view, по сути он содержит в себе бизнес-объекты и обеспечивает работу с ними. Подмена модели другими объектами, не связанными с моделью выглядит странно.
0
«Либо вы работаете над гигантскими проектами»
Достаточно большим, чтобы компиляция шла долго. Обрыв ее на ранней стадии полезен.
«Но MVVM в первую очередь относится к Microsoft, WPF, Silverlight, XAML.»
Он немножко старше.
«Вы имели в виду объект, который оборачивает View model, а не порождает?»
Я имею в виду именно порождает. Я просто не считаю, что изменения в viewmodel должны всегда сразу идти в связанную domain model.
Достаточно большим, чтобы компиляция шла долго. Обрыв ее на ранней стадии полезен.
«Но MVVM в первую очередь относится к Microsoft, WPF, Silverlight, XAML.»
Он немножко старше.
«Вы имели в виду объект, который оборачивает View model, а не порождает?»
Я имею в виду именно порождает. Я просто не считаю, что изменения в viewmodel должны всегда сразу идти в связанную domain model.
0
Зарегистрируйтесь на Хабре , чтобы оставить комментарий
Создание динамического прокси-объекта с помощью dynamic типа