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

Комментарии 23

Тема сто раз жевана-пережевана на .NET ресурсах…

ЗЫ. А меня дико бесит this и скобки, который пихают всюду, куда не лень :)
Относительно жеванности — возможно, но я раньше не встречал, да и на Хабре вроде не было.
А this и скобки всюда где не лень — ну, на вкус и цвет, что называется… StyleCop говорит надо, мы говорим — есть!
Так же, кстати, работают строго типизированные helper methods в ASP.NET MVC (типа Html.TextBoxFor(model=>model.Name)). И еще можно заглянуть в ExpressionHelper.GetRouteValuesFromExpression из сборки ASP.NET MVC Futures. Название класса говорит само за себя)
Ох удивили… вопрос уже обсосан до немогу. (Во что превращается Хабр?)
Можна пожалуйста ссылки на обсасывание? Интересно все-таки до чего договорились.
Если вам нужно это использовать в WPF, зачем вы изобретаете велосипед (?), наследуйтесь от DependencyObject и используйте родные для WPF DependenctProperty.
Работает такая схема на порядок быстрее, так как именно под это и заточена.
Вот тут MVVM – Lambda vs INotifyPropertyChanged vs DependencyObject можно посмотреть результаты тестов использования разных методов.
Вы даже не представляете на сколько сильно вы тормозите выполнение своими Expression.
Хотя это еще что, я видел как под это дело рефлексию прикручивали…
Я видел еще более извращенный способ: вместо NotifyPropertyChanged(«name») использовался метод Notify(), который анализировал фрэймы call stack'a, для выяснения из какого свойства его вызвали и вызывал для него NotifyPropertyChanged.

Все хорошо в меру. Если у класса есть много редко вызываемых свойств, я предпочитаю использовать лямбды. Если несколько интенсивно используемых — DependencyObject.
А как с многопоточным кодом?
Посмотрел я эти тесты — слов нет… одни матюки.
Чувак в цикле присваивает одно и то же значение — реализация INotifyPropertyChanged и Lambda как пацаны диспетчят события… В то же время DependencyObject делает это только тогда, когда значение меняется…

Добавил в те циклы динамическое значение:
dependencyObjectViewModel.DummyProperty = «DummyText» + i;

Сразу все стало на свои места:
INotifyPropertyChanged = 20 секунд
Lambda = 34 секунды
DependencyObject = 18 секунд.

Так что разгон про DependencyObject — от лукавого
У него рефлексия только в дебаге работала, и хотя требовала дополнительных телодвижений, типа добавления verify… все же быстрее чем expression
Всё хорошо, но у меня код который должен работать на .net 2.0 и без поддержки рефлексии.
Потому к каждому класу с пропертями есть зеркальный клас с строковыми константами названиями пропертей(атрибуты нельзя, потомуму что изза драконовской секьрити не загружаются).
Потому вызов выглядит что-то вроде:
InvokePropertyChanged(EntitityNames.PropertyName);
FindAllRefrences на таком подходе работать будет.
Для начала поправьте заголовок поста, имя события — OnPropertyChanged.
А во вторых, вы понимаете, что каждый вызов

this.OnPropertyChanged(() => this.ShowOverrideButton);
приводит к созданию новой копии делегата, передаваемого в качестве параметра?
1. Спасибо, исправил.
2. Вы имеете ввиду возможные memory leaks из-за сильной связаности?
Нет, я хочу сказать, что для свойства, значение которого часто меняется, будет сгенерирована тонна мусора, по одному экземпляру Delegate на каждый вызов.
Сейчас, что бы было совсем понятно, код

this.OnPropertyChanged(() => this.ShowOverrideButton);
полностью аналогичен коду

this.OnPropertyChanged(new Expression<Func<_type>>(new Func<_type>(() => this.ShowOverrideButton)));
, где _type — тип свойства.
А нет, пардон, всё немного не так, код транслируется в такую вот хитрую штуку:
this.OnPropertyChanged<bool>(Expression.Lambda<Func<bool>>(Expression.Property(Expression.Constant(this, typeof(_owner_type)), (MethodInfo) methodof(_ownerType.get_ShowOverrideButton)), new ParameterExpression[0]));

Т.е. всё еще куда более запущено! При каждом вызове создаётся синтаксическое дерево с использованием рефлексии!
[Conditional(«DEBUG»)], конечно, отчасти дело спасает.
Эх, что-то у меня сутра с мозгами плохо совсем, [Conditional(«DEBUG»)] спасает первую версию, а версию с выражением — нет. ИМХО подобные накладные расходы в релизной версии никак не оправданы.
Мдя… Страшноватенько. Так что, человеческого решения нет?.. Тут выше кто-то кричал о обсосанности темы. Щас спрошу у них.
Меня вопрос заинтересовал, я сам намучался с рефакторингом классов, реализующих INotifyPropertyChanged «штатным» способом. Пока приемлемого решения мне на ум не приходило.
Я тут подумал: на худой конец можно использовать делегаты, а в Prebuild-event'ы засунуть sed который будет перемалывать их в обычные текстовые строки.

… или просто забить, учитывая правило о преждевременной оптимизации.
Да, и по поводу DependencyObject — класс не заменим в ситуациях, когда у объекта много свойств, и у большей части экземпляров объектов значения этих свойств устанавливаются по умолчанию. Это даёт солидную экономию памяти. В купе с механизмом наследования значений и присоединенными свойствами, разработчик получает мощные возможности контроля над значениями свойств.
Однако если у класса мало свойств и их значения почти всегда разные, что очень характерно для классов, реализующих логику приложения или модель данных, то DependencyObject — не лучшее решение. Свойства зависимости хранятся вне экземпляров объектов в глобальной хеш-таблице, и само значение свойства занимает гораздо больше памяти, чем необходимо под экземпляр типа свойства. Поэтому использование INotifyPropertyChanged в модели данных — более чем оправдано.
Кроме того, нельзя получить прямой доступ к значению свойства зависимости из потока, которому не принадлежит DispatcherObject, даже на чтение.
Я решил для себя эту проблему просто переписав propf сниппет. Добавил в сеттере строчку, бросающую PropertyChanged($PropertyName$)
Это решает почти все проблемы, ведь согласитесь, что 90-95% PropertyChanged бросается в сеттерах свойств.
Ну а если надо бросить его откуда-то извне — да, приходится писать ручками. Имхо проще, чем добавлять в каждый проект довольно много кода, плюс строками читаемость для других разработчиков проще.
На самом деле очень неважный вариант.

что мне помешает написать OnPropertyChanged(() => PropertyFromAnotherObject);

Вы перенесли потенциальную ошибку из рантайма в компилтайм, но безопасности не обеспечили…
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации