Мне очень давно не нравилось использование имен в виде строк в биндинге или, например, при реализации интерфейса INotifyPropertyChanged. Очень ненадежно и код так и норовит сломаться при рефакторинге. Вчера мне пришло в голову решение на основе Expression. Наверняка кто-то уже находил такое решение или даже использует его, потому что решение лежит на поверхности. Но мне такой подход до сих пор не попадался, поэтому надеюсь что кому-то мой пост будет интересен.
Итак приведу небольшой класс, глядя на который будет ясно что я имею в виду:
class A1 : INotifyPropertyChanged
{
private string _myProperty;
public string MyProperty
{
get { return _myProperty; }
set
{
_myProperty = value;
InvokePropertyChanged(() => MyProperty);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void InvokePropertyChanged<T>(Expression<Func<T>> property)
{
PropertyChangedEventHandler Handler = PropertyChanged;
if (Handler != null)
{
MemberExpression expression = (MemberExpression) property.Body;
Handler(this, new PropertyChangedEventArgs(expression.Member.Name));
}
}
}
При таком подходе можно быть спокойным во время рефакторинга.
Конечно же меня заинтересовало насколько просядет производительность при таком коде
Здравствуйте, SiAVoL, Вы писали:
SAV>Мне очень давно не нравилось использование имен в виде строк в биндинге или, например, при реализации интерфейса INotifyPropertyChanged. Очень ненадежно и код так и норовит сломаться при рефакторинге.
Вот это и надо лечить, объявляя константы с именами свойств, а не изобретать квадратные колёса.
SAV>Вчера мне пришло в голову решение на основе Expression.
Потеряешь на встраивании. Проверять надо на релизе при запуске не из под отладчика.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>Вот это и надо лечить, объявляя константы с именами свойств, а не изобретать квадратные колёса.
А как константы помогут при рефакторинге? И или предлагается сделать public константу с именем свойства, которую везде и использовать, а при рефакторинге не забывать изменить эту константу руками? По мне эти колеса нисколько не круглее.
_FR>Проверять надо на релизе при запуске не из под отладчика.
я и так проверял на релизе не из-под отладчика.
Здравствуйте, arkhivania, Вы писали:
A>Если вы используете данное в WPF, то в sp1 для .Net 3.5 заработало долгожданное event EventHandler PropertyNameChanged.
Это то, что давно работает в WinForms? Т.е. выделенное это имя конкретного свойства? Если да, то мне подход с INotifyPropertyChanged нравится даже больше. Не надо плодить кучу событий, поэтому код более краткий. К тому же наличие такого события опять же не поможет при рефакторинге, событие все равно надо будет не забыть переименовать руками.
... << RSDN@Home 1.2.0 alpha 4 rev. 1096>>
Re: Удобное получение имени свойства
От:
Аноним
Дата:
18.08.08 12:27
Оценка:
Здравствуйте, SiAVoL, Вы писали:
Предлагаю следующее решение, почти без затрат времени, и писанины меньше:
class A3 : INotifyPropertyChanged
{
private int _myProperty;
public int MyProperty
{
get { return _myProperty; }
set
{
_myProperty = value;
PropertyChanged.Fire(this);
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
static class Utils
{
static System.Collections.Generic.Dictionary<Type, string> dict = new System.Collections.Generic.Dictionary<Type, string>();
public static void Fire(this PropertyChangedEventHandler handler, INotifyPropertyChanged obj)
{
if (handler != null)
{
string propertyName;
if (!dict.TryGetValue(obj.GetType(), out propertyName))
{
dict[obj.GetType()] = propertyName =
(new System.Diagnostics.StackTrace()).GetFrame(1).GetMethod().Name.Substring(4);
}
handler(obj, new PropertyChangedEventArgs(propertyName));
}
}
}
Здравствуйте, <Аноним>, Вы писали:
А>Предлагаю следующее решение, почти без затрат времени, и писанины меньше:
либо я чего-то не понял, либо предполагается, что у каждого класса будет не более одного свойства с поддержкой уведомления об изменениях.
Здравствуйте, SiAVoL, Вы писали:
SAV>Здравствуйте, <Аноним>, Вы писали:
А>>Предлагаю следующее решение, почти без затрат времени, и писанины меньше: SAV>либо я чего-то не понял
Видимо вы не поняли, имя свойства берется из стека.
А про то что INotifyPropertyChanged типа хорошее решение (меньше кода и не нужно плодить ивентов), думаю что с этим согласится меньше 1% проектировщиков.
Здравствуйте, SiAVoL, Вы писали:
SAV>... у каждого класса будет не более одного свойства с поддержкой уведомления об изменениях.
увы это так
будем искать другое решение
Здравствуйте, Аноним, Вы писали:
А>Предлагаю следующее решение, почти без затрат времени, и писанины меньше: А>Вот мои результаты тестирования: А>Получается чуть медленнее оригинального (но с пустыми обработчиками !!!!). А>Но если свойство имеет тип System.Int32, результаты значительно отличаются: А>т.е. улучшение в 100 раз при безопасном рефакторинге
А сколько получится, ежели проводить измерения при глубине стека вызовов в районе 30+ ?
мне кажется очень красивые колеса, элегантные. Возможно ездить на них и медленнее, но как средство от головной боли очень даже — какая разница какие колеса ;)
Здравствуйте, arkhivania, Вы писали:
A>Видимо вы не поняли, имя свойства берется из стека.
я все понял. Только суть не в этом, там словарь с ключем по типу. Т.е. вызвали метод из одного свойства, в словарь записался тип и имя свойства. Вызвали из другого свойства, полетело событие с именем первого свойства. Мне кажется, что это неправильное поведение. Впрочем автор со мной уже согласился уже согласился
A>А про то что INotifyPropertyChanged типа хорошее решение (меньше кода и не нужно плодить ивентов), думаю что с этим согласится меньше 1% проектировщиков.
А чем решение плохо? У него есть своя обширная ниша — бизнес-объекты. Для ивента на каждое свойство нужно:
— во-первых писать эти ивенты. Да, можно сделать снипеты, шаблоны и пр.
— во-вторых на каждое свойство надо держать поле-делегат. А они память кушают. А подписываться на все события нужно далеко не всегда. Да, можно переопределить add/remove для события.
Все решаемо, но для чего плодить сложности если для классов бизнес-объектов INotifyPropertyChanged нужен в основном для связи с UI?
Для компонентов, не спорю, часто отдельные события предпочтительнее.
Здравствуйте, SiAVoL, Вы писали:
SAV>Видно, что "новый" подход примерно в 20 раз медленнее классического. Но в принципе для любителей "не экономить на спичках", чем не вариант?
Сам такой тоже писал пол года назад
Учитывая, что подписчик на данное свойство обычно использует reflection чтобы вытащить новое значение свойства — разница в скорости совсем будет не заметна
Можно ещё лямбда-выражения сделать статическими членами класса, а в хелпере использовать хеш-таблицу для хранения уже вычисленных имён свойств.
Но я думаю, что в типичных сценариях эта дополнительная писанина/оптимизация себя не оправдает.
Вообще если нужно сверхскоростное решение, то придумать такое могу ), правда с небольшими ограничениями.
1) Свойство помечается неким своим аттрибутом.
2) Далее в сеттере используете например свой способ (или любой "медленный")
-- до этого места всё как обычно, работаете, отлаживаете, а вот в релизе
3) Пишете утилиту, которая при помощи Cecil заменяет медленный способ на стандартный у всех свойств помеченных вашим аттрибутом.
Здравствуйте, arkhivania, Вы писали:
A>3) Пишете утилиту, которая при помощи Cecil заменяет медленный способ на стандартный у всех свойств помеченных вашим аттрибутом.