It all started the same as last time, quite prosaically: I had to develop * -13 ViewModels for my MVVM application.
In order for them to work optimally as ViewModel-i, my classes had to inherit from DependencyObject or implement the INotifyPropertyChanged (INPC) interface, which was broken down to holes.
It has long been no secret to anyone that DependencyProperty is more deceptive than the manual implementation of INPC. My tests show that writing to DependencyProperty is ~ 13 times slower than manual implementation. Therefore, I, as an incurable optimizer, tend exactly to INPC. Moreover, the INPC support code looks more logical and more organic than the description of DependencyProperties.
Many articles are written on how to facilitate the implementation of INPC. This is the variant with the StackTrace research, this is also the variant with Lambda-methods, this is also code-snippets as a personal code-monkey, this is also Resharper, as a panacea for refactoring errors. All of these options require a lot of unnecessary gestures, and I, as an irremovable routine affairs optimizer, do not like it.
')
Here, for example, is an implementation using StackTrace:
public sealed class StackTraceNPC : INotifyPropertyChanged { string _myProperty; public string MyProperty { get { return _myProperty; } set { if (_myProperty == value) return; _myProperty = value; RaisePropertyChanged(); } }
Here is an example with an expression tree, each time created by the compiler in the code:
public sealed class LambdaNPC : INotifyPropertyChanged { string _myProperty; public string MyProperty { get { return _myProperty; } set { if (_myProperty == value) return; _myProperty = value; RaisePropertyChanged(() => this.MyProperty); } } void RaisePropertyChanged<T>(Expression<Func<T>> raiser) { var e = PropertyChanged; if (e != null) { var propName = ((MemberExpression)raiser.Body).Member.Name; e(this, new PropertyChangedEventArgs(propName)); } } public event PropertyChangedEventHandler PropertyChanged; }
And here are the performance results of the above-mentioned INPC implementations:

Incorrigible optimizers, looking at these numbers, with horror erased from the memory and StackTrace, and Lambda options. It is clear that setters are not called so often to seriously think about their performance, but if we are talking about ViewModels to the DataGrid, or a serious number of fields, then these brakes can float to the surface. In addition, it is not so much a convenient call to RaisePropertyChanged, but more about optimizing all the hemorrhoids that are associated with it, including checking for a change in the field and other scribbling as a type property literal.
One of the worthy options would be an PostSharp-based AoP approach, but one glance through the Reflector on the IL-code received after compilation is enough to understand that we are not on the way with PostSharp either.
Here it would be time to twist ... But inspired by articles about Mono.Cecil about the
injection of MSIL code into a third-party assembly using Mono.Cecil , I decided to solve this problem once and for all.
To begin with, I will give an example of how
WAS :
public class MyViewModel: PropertyChangedBase { string _stringProperty; public string StringProperty { get { return _stringProperty; } set { if (_stringProperty == value) return; _stringProperty = value; RaisePropertyChanged("StringProperty"); } } object _objectProperty; public object ObjectProperty { get { return _objectProperty; } set { if (_objectProperty == value) return; _objectProperty = value; RaisePropertyChanged("ObjectProperty"); } } }
Now, an example of how it has
become :
public class MyViewModel: PropertyChangedBase { public string StringProperty { get; set;} public object ObjectProperty { get; set;} }
And where is the implementation of INPC, you ask, and you will be right. Kind of Magic? Namely, the
Kind of Magic MSBuild task. This is the name of this open-source codeplex project.
The whole secret is in the base class PropertyChangedBase, each of us has its own version :)
Let's see what is so special about him:
[Magic] public abstract class PropertyChangedBase : INotifyPropertyChanged { protected virtual void RaisePropertyChanged(string propName) { var e = PropertyChanged; if (e != null) e(this, new PropertyChangedEventArgs(propName));
With the exception of the Magic attribute, everything else looks more or less in order. Let's look at the MagicAttribute, which is described in the same assembly as our MyViewModel class.
class MagicAttribute: Attribute {}
One line, you ask? Exactly. It is enough to define an attribute with the name MagicAttribute in your assembly, apply it to the base or any class that implements INPC. In this case, all public properties of these classes and their heirs will become INPC-compatible. You can apply directly to the properties of your INPC class, then only these properties will become INPC-compatible.
And adding this attribute:
class NoMagicAttribute: Attribute {}
You can exclude classes and properties from the magical implementation of INPC.
It’s not worth worrying about extra kilobytes of code, after compiling your build, there will be no trace of these attributes, to make sure Reflector will help you.
Now a little about how it works.
- Everything happens at compile time. More precisely after compilation, but before the signature of the assembly. Those. in runtime we get maximum performance (in most cases, even faster than handwritten code).
- KindOfMagic literally writes to the setter what we ourselves are lazy to write, thereby reducing the load on the fingers, Resharper, the code editor and nerves.
- KindOfMagic makes the properties INPC-compatible and only that. And it does it optimally from the point of view of IL, quickly and transparently. Associated PDB files are also transformed, so there are no problems with debugging “enchanted” properties.
- KindOfMagic calls RaisePropertyChanged only when the property has really changed. Verification code old-new is generated depending on the type of property. Any type is supported, including Nullable <T>.
- KindOfMagic supports both Silverlight and .NET projects.
- KindOfMagic uses Mono.Cecil for code injection. Thanks to Miguel and K.
Well, now, we meet the winner:

Here you can download
KindOfMagic , and
here lies a test project for the doubters. Results obtained under Win7x64, Core2 Quad @ 2.4GHz.
UPDATE 1Honestly, I did not expect such a devastating result, the resulting IL is not very much different.On closer inspection, a bug was found, the bug was successfully fixed. The result of KindOfMagic equaled the handwritten code, as expected.
Real miracles do not happen, sometimes something as a result :)