Implementing INotifyPropertyChanged with PostSharp

Chris Rodgers · 24 September, 2014

There are many, many posts about implementing INotifyPropertyChanged and almost as many methods. So of course, here’s another.

A lot of the methods mean you can’t use automatic properties. This bothers me. Firstly, I’m inherently lazy (aren’t we all). Every saved key press warms my heart. Of course, you can create snippets that save you time (ReSharper does, admittedly make it very easy) but there’s more; Automatic properties are, I think, much neater looking. The extra field, plus n lines of implementation take up space and I’ve just come to think of it as cruft.

Some methods use weaving to inject IL code into your assembly at compile time, or run time. This is better, but the best methods use Aspect Oriented Programming to intercept the call. This is great, as it keeps things DRY, and allows nice neat automatic properties.

Now, one note before I get into it. I use PostSharp as my AOP platform of choice, and it already provides a (very, very good) INotifyPropertyChanged implementation already. The first problem with this is that their patterns library isn’t free. They kindly allow 10 uses per project, but more than that will cost you. The second problem, in my opinion, is that an attribute has to be applied to each class, which I don’t like. It’s possible that it can be applied using an assembly attribute, in the same manner as my example, but all the examples I’ve seen show it applied to the class. One thing the PostSharp implementation does better, however, is also raise events for other properties when a property or field it relies on is changed.

So getting in to it, first we define a base class that implements INotifyPropertyChanged (and, optionally, INotifyPropertyChanging) and exposes methods to raise the events. I’ve made the methods to raise the events explicit implementations, while the events themselves are implicit implementations. Some frameworks, such as Caliburn.Micro and ReactiveUI (you can see its influence on my naming here) provide base classes for your view models already.

public interface IReactiveObject : INotifyPropertyChanging, INotifyPropertyChanged
{
    void RaisePropertyChanging(string propertyName);
    void RaisePropertyChanged(string propertyName);
}

public abstract class ReactiveObject : IReactiveObject
{
    void IReactiveObject.RaisePropertyChanging(string propertyName) =>
        PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName));

    public event PropertyChangingEventHandler PropertyChanging;

    void IReactiveObject.RaisePropertyChanged(string propertyName) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

    public event PropertyChangedEventHandler PropertyChanged;
}

The aspect is a pretty simple subclass of Post Sharps built in LocationInterceptionAspect. We override the OnSetValue method and complete our regular logic.

[Serializable]
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class NotifyPropertyChangedAspect : LocationInterceptionAspect
{
    public override void OnSetValue(LocationInterceptionArgs args)
    {
        if (args.GetCurrentValue() != args.Value)
        {
            if (args.Location.PropertyInfo != null && args.Instance is IReactiveObject reactive)
            {
                reactive.RaisePropertyChanging(args.Location.PropertyInfo.Name);
                args.ProceedSetValue();
                reactive.RaisePropertyChanged(args.Location.PropertyInfo.Name);
            }
            else
            {
                args.ProceedSetValue();
            }
        }
    }
}

We can then apply this advice using a pointcut. This one selects all public members of public types inside the “Example.ViewModels” namespace.

[assembly: NotifyPropertyChangedAspect(
        AttributeTargetTypes = "Example.ViewModels.*",
        AttributeTargetTypeAttributes = MulticastAttributes.Public,
        AttributeTargetMemberAttributes = MulticastAttributes.Public)]

Finally, we can add a view model that inherits from our base class and be confident that our auto properties will notify when they’re changed, without having to include any plumbing code, or any attributes on the class or properties themselves.

namespace Example.ViewModels
{
    public class MyViewModel : ReactiveObject
    {
        public string MyProperty { get; set; }
    }
}

Twitter, Facebook