📜 ⬆️ ⬇️

Clean code under the flag of AOP and hateful #WarningReal estateChange

Yielding to the general hysteria on Habré,

(namely, “I’m warning Real Estate Has Changed” translates Guglex’s beloved “I Notify Property Changed”) about change notifications. I decided to look at how far humanity has advanced in the invention of bicycles.

OnPropertyChanged ("Property")
OnPropertyChanged (() => Property)
SetProperty (ref storage, value)
[CallerMemberName]
[Magic]
AOP of various stripes
even offer roslyn.codeplex.com/discussions/550266 why m not.
Still the coolest of all is nameof (Property) - carefully sharpe 6.
The consequences of hysteria are expressed in the following works.
habrahabr.ru/post/199378
habrahabr.ru/post/246469
habrahabr.ru/post/95211
Personally, I'm happy with the OnPropertyChanged (nameof (Property)) and OnPropertyChanged (() => Property options, the first option is faster.
But most often I use SetProperty (ref storage, value) , a boxed version of BindableBase .
Hobrazhitel Scrooge2 posted
Stop inventing bikes.
Use the usual INotifyPropertyChanged hands will not disappear, without any but.

I fully support, but ... NO.
Well, a file and a sledgehammer.
I'm for clean code! Logging, interception exceptions, checking access rights, all kinds of notifications, etc. - smoke code. Clean the code will allow the ancient knowledge of the template Proxy, for example habrahabr.ru/post/88722 .
Complicating my life and will use classes and not interfaces.
public class Data { public virtual int Value { get; set; } public virtual string Source { get; set; } } 

Notifications, nothing personal
 class BindableObject : BindableBase { public new bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null) { return base.SetProperty<T>(ref storage, value, propertyName); } } 

And the deputy himself, who took over all the dirty work
 public class ProxyData : Data, INotifyPropertyChanged { Data _target; BindableObject _notifier; public ProxyData(Data target) { _target = target; _notifier = new BindableObject(); } public override int Value { set { int newValue = 0; if (_notifier.SetProperty(ref newValue, value)) base.Value = newValue; } } public override string Source { set { string newSource = null; if (_notifier.SetProperty(ref newSource, value)) base.Source = newSource; } } public event PropertyChangedEventHandler PropertyChanged { add { _notifier.PropertyChanged += value; } remove { _notifier.PropertyChanged -= value; } } } 

I will create a console application, where, if not in the console, check Warning. Real EstateChanged
 data = new ProxyData(new Data()); (data as INotifyPropertyChanged).PropertyChanged += (s, e) => { Console.WriteLine(string.Format("Property {0} changed!", e.PropertyName)); }; data.Value = 10; data.Source = "List"; 

Pros : the purest Data class, everything is simple and clear, you can create a bunch of different proxy logger, access, etc., combine, safely remove and add different functionality not related to the direct operation of the application.
Disadvantages : It's useless, I practically turned the substitution of model view into a model, having received another Model link -> ViewModel -> Proxy -> View, one of the ViewModel assignments - notification, of course, you can leave data preparation in vm ... Plus, the whole code even more, although it seems like the responsibility of vm has decreased, oh solid SOLID.
Hysteria! The time has come to AOP, quite a lot has been written about aop and I will not dwell on theory.
I work with IUnityContainer , because it can be considered boxed, interacts well with Prism .
And here is the universal behavior of the notifier, horror
 class NotifyPropertyChangedBehavior : IInterceptionBehavior, INotifyPropertyChanged { static readonly MethodBase _add; static readonly MethodBase _remove; static NotifyPropertyChangedBehavior() { var methods = typeof(INotifyPropertyChanged).GetMethods(); _add = methods[0]; _remove = methods[1]; } public IEnumerable<Type> GetRequiredInterfaces() { return Type.EmptyTypes; } public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { IMethodReturn result = null; if (IsPropertyChanged(input)) if (SubscribeUnsubscribe(input)) result = input.CreateMethodReturn(null); else { PropertyInfo property; if (IsSetMethodCalled(out property, input)) result = SetValue(property, input, getNext); } return result ?? getNext()(input, getNext); } public bool WillExecute { get { return true; } } public event PropertyChangedEventHandler PropertyChanged; /// <summary> ///    /// </summary> /// <returns></returns> bool IsSetMethodCalled(out PropertyInfo property, IMethodInvocation input) { string propertyName = input.MethodBase.Name.TrimStart("set_".ToArray()); property = input.Target.GetType().GetProperty(propertyName); return property != null; } /// <summary> ///  /// </summary> /// <returns></returns> IMethodReturn SetValue(PropertyInfo property, IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { var oldValue = property.GetValue(input.Target, new object[0]); var newValue = input.Arguments[0]; IMethodReturn result = null; //       if (!Equals(oldValue, newValue)) { result = getNext()(input, getNext); if (PropertyChanged != null) PropertyChanged(input.Target, new PropertyChangedEventArgs(property.Name)); } else result = input.CreateMethodReturn(null); return result; } /// <summary> ///    INotifyPropertyChanged /// </summary> bool SubscribeUnsubscribe(IMethodInvocation input) { if (input.MethodBase == _add) { PropertyChanged += (PropertyChangedEventHandler)input.Arguments[0]; return true; } else if (input.MethodBase == _remove) { PropertyChanged -= (PropertyChangedEventHandler)input.Arguments[0]; return true; } return false; } /// <summary> ///     INotifyPropertyChanged /// </summary> bool IsPropertyChanged(IMethodInvocation input) { return input.Target is INotifyPropertyChanged; } } 

Well, a piece of code where it all connects
 IUnityContainer container = new UnityContainer(); container.AddNewExtension<Interception>(); container.RegisterType<Data>(new Interceptor<VirtualMethodInterceptor>(), new InterceptionBehavior<NotifyPropertyChangedBehavior>() , new AdditionalInterface<INotifyPropertyChanged>()); var data = container.Resolve<Data>(); (data as INotifyPropertyChanged).PropertyChanged += (s, e) => { Console.WriteLine(string.Format("Property {0} changed!", e.PropertyName)); }; data.Value = 10; data.Source = "List"; data.Value = 10; Console.ReadKey(); 

Result ----->
image
bun for especially lazy
 public static class Extensions { public static IUnityContainer RegisterViewModel<T>(this IUnityContainer container) where T : class { container.AddNewExtension<Interception>(); return container.RegisterType<T>(new Interceptor<VirtualMethodInterceptor>(), new InterceptionBehavior<NotifyPropertyChangedBehavior>() , new AdditionalInterface<INotifyPropertyChanged>()); } } 

minimize registration
 container.RegisterViewModel<Data>(); 

You can not leave illusions, based on all the same Deputy, but we do not know about him TssssSSssssss.
Pros : now you can make a VM out of everything that has virtual properties, complete minimization of the code, a pure class without Warning Real estateIt has changed, all sorts of attributes and the like.
Cons : absolutely not easy, you need to understand the container and its capabilities aop, not clear in its pure form, perceived as magic, a lot of reflexion is not for the faint of heart, having sifted through performance.
After a small experiment on the staff, the result was deplorable, the programmers were in pain when I asked to figure out how it works.
But it works.
In general, they didn’t introduce it and almost gave it to the righteous fire, aop is cool, but it all depends on the level of the team and their desire for confusion.

PS Anyone who is afraid of this tin I recommend OnPropertyChanged (nameof (Property)) - optimal for all indicators, except that you need to prescribe it with HANDS. Boo!

')

Source: https://habr.com/ru/post/281294/


All Articles