Many .NET developers, who used WPF, Silverlight or Metro UI in their practice, somehow had the question “how can I simplify the implementation of INotifyPropertyChanged interface and properties, which changes should be signaled?”.
The simplest "classic" version of the description of a property that supports notification of its change is as follows:
public string Name { get { return _name; } set { if(_name != value) { _name = value; NotifyPropertyChanged(“Name”); } } }
In order not to repeat similar strings in each setter, you can make an auxiliary function. Moreover - starting with .NET 4.5, it is possible to use the [CallerMemberName] attribute in it, in order not to explicitly specify the name of the property that calls it. But this does not solve the main problem - all the same, for each new property it is necessary to explicitly describe the field and the getter with the setter. Such mechanical work is uninteresting, tedious and can lead to errors when copying and pasting.
I would like a bit of “magic”, which will allow with a little effort (for example, with a single line of code) to make this class compatible with INotifyPropertyChanged:
')
public class Person { public string FirstName { get; set; } public string LastName { get; set; } public DateTime Birth { get; set; } }
It should be noted that this is not the only task in which I would like to simplify my life while adding end-to-end functionality. Typical tasks of logging of called methods of a certain class, measurements of their runtime, checking the availability of rights to call are all that require a common solution, eliminating the need to write similar pieces of code in every place where it is needed.
A more or less experienced developer will immediately say - “It's the same aspect-oriented programming!” And he will be right. And if you start listing existing libraries for the .NET platform, in which in one way or another it is possible to use AOP, the list will not be so short: PostSharp, Unity, Spring .NET, Castle Windsor, Aspect .NET ... And this not all, but here you should think about the mechanisms that implement the insertion of end-to-end functionality, about their advantages and disadvantages. There are two main ways:
- Compile substitution (PostSharp)
- Generating proxy classes at runtime (Unity, Spring .NET, Castle Windsor)
Substitution at compile time is the most profitable way, since it does not require any additional computational power when executing a program, which is especially important for mobile devices. The usual generation of proxy classes is easier to implement, but in addition to the computational costs, it also has limitations - methods or properties must be contained in the interface or be virtual so that they can be intercepted through the proxy class.
PostSharp offers very large possibilities for using aspect-oriented programming, but it is a commercial product, which for many projects may be unacceptable. As an alternative, we have developed and continue to improve the
Aspect Injector - a framework that allows you to apply aspects at the compilation stage and has a simple, but at the same time flexible interface.
The simplest example of using Aspect Injector is logging method calls. First you need to describe the aspect class:
public class MethodTraceAspect { [Advice(InjectionPoints.Before, InjectionTargets.Method)] public void Trace([AdviceArgument(AdviceArgumentSource.TargetName)] string methodName) { Console.WriteLine(methodName); } }
This description suggests that when applying this aspect to any other class, at the beginning of each of its public methods, a call to Trace () will be added with the name of the method itself as a parameter.
[Aspect(typeof(MethodTraceAspect))] public class Target { public void Create() { } public void Update() { } public void Delete() { } }
After such a declaration, Create, Update, Delete will print their names to the console on each call. It should be noted that the Aspect attribute can be applied not only to classes, but also to specific members of a class, if a “point insert” is needed. Here is an example of decompiled code obtained after compiling the example above:
public class Target { private readonly MethodTraceAspect __a$_MethodTraceAspect; public void Create() { this.__a$_MethodTraceAspect.Trace("Create"); } public void Update() { this.__a$_MethodTraceAspect.Trace("Update"); } public void Delete() { this.__a$_MethodTraceAspect.Trace("Delete"); } public Target() { this.__a$_MethodTraceAspect = new MethodTraceAspect(); } }
You may also notice here that the Aspect attribute was deleted during code generation, which allows the resulting assembly not to refer to the Aspect Injector assembly.
If you go back to the original task of implementing the INotifyPropertyChanged interface, you can create and successfully use the following aspect using Aspect Injector:
[AdviceInterfaceProxy(typeof(INotifyPropertyChanged))] public class NotifyPropertyChangedAspect : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged = (s, e) => { }; [Advice(InjectionPoints.After, InjectionTargets.Setter)] public void RaisePropertyChanged( [AdviceArgument(AdviceArgumentSource.Instance)] object targetInstance, [AdviceArgument(AdviceArgumentSource.TargetName)] string propertyName) { PropertyChanged(targetInstance, new PropertyChangedEventArgs(propertyName)); } }
All public properties of all classes to which this aspect will be tied will RaisePropertyChanged at the end of the setter. Moreover, the interface specified in the AdviceInterfaceProxy attribute will be added to the class by the framework during compilation.
On
the project page you can find more detailed information about the attributes and their parameters currently available. We would be grateful for any feedback and suggestions on the development of Aspect Injector!