Здравствуйте, arkhivania, Вы писали:
A>интересно, только отлаживаться так не очень удобно я так понимаю. Но вообще идея такая же.
Отлаживается, на удивление, гладко.
При трассировке точка выполнения заходит везде где нужно, без глюков.
Народ, не слыхавший об инжекции, впадает в ступор.
Здравствуйте, drol, Вы писали:
D>А сколько получится, ежели проводить измерения при глубине стека вызовов в районе 30+ ?
без кэширования — на порядок медленнее чем у SiAVoL
с кэшированием смысла нет (работает только для одного свойства )
Как дети малые, честное слово
От рутины спасает аспектно-ориентированное программирование.
Это не фрагмент из реального проекта (там почти то же, только посложнее), а из головы. так что sorry за ашыпки:
C# 2.0
Допустим, базовый класс всех сущностей:
public abstract class Entity : INotifyPropertyEvent
{
...
void OnPropertyChanged(string propertyName);
}
Его наследник:
public class Person : Entity
{
private string name;
public virtual string Name
{
get { return name; }
set { name = value; }
}
}
Аспект, срабатывающий при вызове любого виртуального (или интерфейсного, или переопределенного) метода или свойства.
internal class PropertyCyhangedInterceptor : IMethodInterceptor
{
public object Invoke(Invocation invocation)
{
object result = invocation.Proceed();
MethodInfo method = invocation.Method;
if (IsPropertySetter(method))
{
Entity entity = invocation.Target;
string propertyName = GetPropertyName(method);
entity.OnPropertyChanged(propertyName);
}
}
}
Репозиторий, из которого извлекаются экземпляры нашей сущности, уже с внедренными аспектами.
Person person = entityManager.Find<Person>(id);
person.Name = "Vassily";
В момент присвоения происходит генерация события из интерфейса INotifyPropertyEvent.
Скорость — где-то 90% от номинала. Хотя... может я применял какие-то оптимизации... не помню. Но не важно, смысл не меняется.
Давным давно используется, проблем никаких, рекомендую. Нет, вру: иногда забывают объявить свойство виртуальным, но это лечится.
На опушке за околицей мужики строили коровник.
Работали споро и весело. Получалось х**во.
Здравствуйте, SiAVoL, Вы писали:
SAV>Мне очень давно не нравилось использование имен в виде строк в биндинге или, например, при реализации интерфейса INotifyPropertyChanged. Очень ненадежно и код так и норовит сломаться при рефакторинге. Вчера мне пришло в голову решение на основе Expression. Наверняка кто-то уже находил такое решение или даже использует его, потому что решение лежит на поверхности. Но мне такой подход до сих пор не попадался, поэтому надеюсь что кому-то мой пост будет интересен. SAV>Итак приведу небольшой класс, глядя на который будет ясно что я имею в виду:
Правильной дорогой идете, товарищи. Аналогичные трюки применяют в ASP.NET MVC Framework.
Я бы это назвал "использование лямбд для записи ссылок на мемберы".
Общий принцип: делаем лямбду, которая никогда не вызывается. Вместо этого она используется для интроспекции и получения побочных эффектов.
А теперь попробуем выполнить некоторый оверклокинг:
private static Dictionary<Expression, PropertyChangedEventArgs> _propertyArgs = new Dictionary<Expression, PropertyChangedEventArgs>();
private void InvokePropertyChanged<T>(Expression<Func<T>> property)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler == null)
return;
PropertyChangedEventArgs args;
if (!_propertyArgs.TryGetValue(property, out args))
{
MemberExpression expression = (MemberExpression) property.Body;
args = new PropertyChangedEventArgs(expression.Member.Name);
_propertyArgs[property] = args;
}
handler(this, args);
}
Померишь скорость?
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>А теперь попробуем выполнить некоторый оверклокинг: S>Померишь скорость?
померял. В твоем варианте сильно медленнее даже моего. Все из-за того, что Expression не умеют сравниваться. Сходу я нормального метода сравнения не обнаружил. Поэтому сделал "наивным" методом
string propertyString = property.ToString();if (!_propertyArgs.TryGetValue(propertyString, out args))
{
MemberExpression expression = (MemberExpression)property.Body;
args = new PropertyChangedEventArgs(expression.Member.Name);
_propertyArgs[propertyString] = args;
}
Так кэширование действительно работает. Но толку от него немного:
Здравствуйте, SiAVoL, Вы писали: SAV>померял. В твоем варианте сильно медленнее даже моего. Все из-за того, что Expression не умеют сравниваться.
Не может такого быть!
Как это не умеют сравниваться? Они же ref-типы, значит умеют сравниваться по identity.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, SiAVoL, Вы писали: S>>Как это не умеют сравниваться? Они же ref-типы, значит умеют сравниваться по identity. SAV>не могу знать!
Ты страдаешь ерундой. Какая тебе разница, что вернет Equals?
Ты скомпилируй и замерь тот код, который я привел. Без улучшений.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Ты страдаешь ерундой. Какая тебе разница, что вернет Equals? S>Ты скомпилируй и замерь тот код, который я привел. Без улучшений.
хорошо, вот класс, сделанный из твоего кода
class A3 : INotifyPropertyChanged
{
private static readonly Dictionary<Expression, PropertyChangedEventArgs> _propertyArgs = new Dictionary<Expression, PropertyChangedEventArgs>();
private string _myProperty;
public event PropertyChangedEventHandler PropertyChanged;
public string MyProperty
{
get { return _myProperty; }
set
{
_myProperty = value;
InvokePropertyChanged(() => MyProperty);
}
}
private void InvokePropertyChanged<T>(Expression<Func<T>> property)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler == null)
return;
PropertyChangedEventArgs args;
if (!_propertyArgs.TryGetValue(property, out args))
{
MemberExpression expression = (MemberExpression)property.Body;
args = new PropertyChangedEventArgs(expression.Member.Name);
_propertyArgs[property] = args;
}
handler(this, args);
}
}
Здравствуйте, SiAVoL, Вы писали:
SAV>Вот еще один маленький фрагмент, для проверки работает ли кэширование
Ага, интересно. Я полагал, что компилятор построит дерево статически и будет его повторно использовать в вызове.
Похоже, он так не сделал. Тогда простой способ — в том, чтобы принудительно применять один и тот же объект, но это будет некрасиво.
Значит, надо думать еще.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, SiAVoL, Вы писали:
SAV>>Мне очень давно не нравилось использование имен в виде строк в биндинге или, например, при реализации интерфейса INotifyPropertyChanged. Очень ненадежно и код так и норовит сломаться при рефакторинге. Вчера мне пришло в голову решение на основе Expression. Наверняка кто-то уже находил такое решение или даже использует его, потому что решение лежит на поверхности. Но мне такой подход до сих пор не попадался, поэтому надеюсь что кому-то мой пост будет интересен. SAV>>Итак приведу небольшой класс, глядя на который будет ясно что я имею в виду: S>Правильной дорогой идете, товарищи. Аналогичные трюки применяют в ASP.NET MVC Framework. S>Я бы это назвал "использование лямбд для записи ссылок на мемберы". S>Общий принцип: делаем лямбду, которая никогда не вызывается. Вместо этого она используется для интроспекции и получения побочных эффектов.
S>А теперь попробуем выполнить некоторый оверклокинг: S>
Здравствуйте, Curufinwe, Вы писали:
C>Что же Вы два разных экземпляра сравниваете? Сделайте для каждого проперти один раз выражение и поместите в статичечкую переменную.
Дык как же в статическую переменную можно поместить ссылку на выражение с нестатическим свойством?
плавали, влипли
Здравствуйте, adanov, Вы писали:
A>Здравствуйте, Curufinwe, Вы писали:
C>>Что же Вы два разных экземпляра сравниваете? Сделайте для каждого проперти один раз выражение и поместите в статичечкую переменную.
A>Дык как же в статическую переменную можно поместить ссылку на выражение с нестатическим свойством? A>плавали, влипли
Здравствуйте, akarinsky, Вы писали:
A>Давным давно используется, проблем никаких, рекомендую. Нет, вру: иногда забывают объявить свойство виртуальным, но это лечится.
При этом появляется другие проблемы — навскидку, проконтролировать, что все объекты создаются через фабрику, нужно помнить, что имя класса будет оличаться, делать все свойства виртуальными.
Здравствуйте, akarinsky, Вы писали:
A>Здравствуйте, SiAVoL, Вы писали:
A>Как дети малые, честное слово A>От рутины спасает аспектно-ориентированное программирование. A>Это не фрагмент из реального проекта (там почти то же, только посложнее), а из головы. так что sorry за ашыпки:
я тебе тоже могу привести пример
public abstract class Person : EditableObject<Person>
{
public abstract string Name { get; set; }
public abstract int Age { get; set; }
}
...
Person person = Person.CreateInstance();
и получить класс с поддержкой INotifyPropertyChanged, отмены или принятия изменений, флагом IsDirty, валидацией на атрибутах, также через атрибуты легко привинчиваются аспекты типа логирования вызовов, кэширования и прочее. Все что нужно для этого щастья это сделать линк на BLToolKit.
Только вот не везде такие решения нужны и оправданы. А красиво жить хочется всегда.
Здравствуйте, akarinsky, Вы писали:
A>Здравствуйте, SiAVoL, Вы писали:
A>Как дети малые, честное слово A>От рутины спасает аспектно-ориентированное программирование. A>Это не фрагмент из реального проекта (там почти то же, только посложнее), а из головы. так что sorry за ашыпки:
я тебе тоже могу привести пример
public abstract class Person : EditableObject<Person>
{
public abstract string Name { get; set; }
public abstract int Age { get; set; }
}
...
Person person = Person.CreateInstance();
и получить класс с поддержкой INotifyPropertyChanged, отмены или принятия изменений, флагом IsDirty, валидацией на атрибутах, также через атрибуты легко привинчиваются аспекты типа логирования вызовов, кэширования и прочее. Все что нужно для этого щастья это сделать линк на BLToolKit.
Только вот не везде такие решения нужны и оправданы. А красиво жить хочется всегда.