Thursday, June 27, 2013

INotifyPropertyChanged implementation

If you created any XAML based application (WPF, Silverlight, Windows Phone, Windows App Store) using MVVM pattern than you are probably familiar with INotifyPropertyChanged interface.  However to implement the interface most developers are faced with two problem that they should be concerned with.  Consider the follow snippet of code:
private string _propertyA;
public string PropertyA
{
    get { return _propertyA; }
    set
    {
        // P1 repetitive code
        if (value == _propertyA) return;
        _propertyA = value;
        // P2 "magic" string
        RaisePropertyChanged("PropertyA");
    }
}

private void OnPropertyChanged(object sender, PropertyChangeEventArg e)
{
 // P2 "magic" string usage
 if (e.PropertyName == "PropertyA")
  HandlePropertyAChanged();
}

There are two basic problem with this implementation. The first problem is the repetitive code, the 3 lines of code inside set method is pretty much the same for majority of the property and in most implementation it is simply copied and pasted everywhere.  The second issue, magic string, is a slightly more challenging problem to deal with. The big deal with the magic string is when code changes and the name of the property changes the programmer has to not only change the string inside the property's setter but everywhere that listens to the PropertyChanged event.

The first problem can be solved by encapsulate the code into a generic method, and there are a few different strategies for dealing with the second problem and we will explore the pros and cons of different solutions. The second problem can be solved by using constant member for the string thus change only need to occur at one place and we gain the refactor support or using reflection. There's also other implementation by leveraging new .Net feature of optional method parameter mark with attribute. What we will do in this article is to explore a few different implementations that attempt to address these two issue and compare the performance of each implementation. As a bonus there is also and implementation that uses weak event handler to address the possibility of memory leak.

Implementations

Simple

The simple implementation is included here as reference. It does use the constant string to represent property name for refactor support.

Setter

The setter class introduce a helper method to set property value and raise property changed event.
protected bool Set<T>(string propertyName, ref T field, T value)
{
    if (field == null || EqualityComparer<T>.Default.Equals(field, value)) { return false; }
    field = value;
    RaisePropertyChanged(propertyName);
    return true;
}
The property setter than simply become
private DateTime _time;
public DateTime Time
{
    get { return _time; }
    set { Set(TimeProperty, ref _time, value); }
}
This help to reduce boilerplate code and make sure the behaviors are consistent.

Delegate Setter

The delegate setter implementation uses Action<T> lambda to update the value so it doesn't have to have the field parameter passed in as reference, the set helper looks like this
protected bool Set<T>(string propertyName, Action<T> setter, T field, T value)
{
    if (field == null || EqualityComparer<T>.Default.Equals(field, value)) { return false; }
    setter(value);
    RaisePropertyChanged(propertyName);
    return true;
}
To use the helper method we would need to create an inner class that holds the property value
private class Inner : IDisplayText
{
    public string DisplayText { get; set; }
    public DateTime Time { get; set; }
    public bool Status { get; set; }
    public int Count { get; set; }
}
private readonly Inner _inner = new Inner();
The property setting would then call the helper method like this
public DateTime Time
{
    get { return _inner.Time; }
    set { Set(TimeProperty, (t) => _inner.Time = t, _inner.Time, value); }
}

Lambda

The lambda method utilize the Expression<Func<T>> to capture the property that's been updated and does not rely on constant member for property name. Again a helper method is introduced to encapsulate the property changed behavior. See conclusion about performance difference between .net 4.0 to .net 4.5.
protected bool Set<T>(Expression<Func<T>> expression, ref T field, T value)
{
    if (field == null || EqualityComparer<T>.Default.Equals(field, value)) { return false; }
    field = value;
    RaisePropertyChanged(GetPropertyName(expression));
    return true;
}

protected string GetPropertyName<T>(Expression<Func<T>> expression)
{
    MemberExpression memberExpression = (MemberExpression)expression.Body;
    return memberExpression.Member.Name;
}

Field

The field implementation introduces a helper class to capture the backing field for the property
protected class Field<T>
{
    public string PropertyName;
    public T Value;

    public Field(string propertyName)
    {
        PropertyName = propertyName;
    }

    public static implicit operator T(Field<T> t)
    {
        return t.Value;
    }
}
Again with a helper method for setting the property
protected bool Set<T>(Field<T> field, T value)
{
    if (field == null || EqualityComparer<T>.Default.Equals(field.Value, value)) return false;
    field.Value = value;
    RaisePropertyChanged(field.PropertyName);
    return true;
}
The definition of a property looks like this
private readonly Field<DateTime> _time = new Field<DateTime>(TimeProperty);
public DateTime Time
{
    get { return _time; }
    set { Set(_time, value); }
}

Lambda Field

Lambda Field implementation looks to improve the runtime performance (see results section) of Lambda implementation by combining Lambda and Field implementation. The Field class definition looks like this
protected class Field<T>
{
    public string PropertyName;
    public T Value;

    public Field(Expression<Func<T>> expression)
    {
        PropertyName = GetPropertyName(expression);
    }

    protected string GetPropertyName<T>(Expression<Func<T>> expression)
    {
        MemberExpression memberExpression = (MemberExpression)expression.Body;
        return memberExpression.Member.Name;
    }

    public static implicit operator T(Field<T> t)
    {
        return t.Value;
    }
}
The helper method is the same as Field implementation and the definition of property is
private readonly Field<DateTime> _time = new Field<DateTime>(() => Time);
public string TimeProperty { get { return _time.PropertyName; } }
public DateTime Time
{
    get { return _time; }
    set { Set(_time, value); }
}

Field2

Field2 is a modified Field implementation to remove constant string for property name. It borrows Lambda implementation's method of determining property name. The constants for property names are changed to static readonly fields of the class and are set at application startup by the static constructor to reduce overhead
public static readonly string TimeProperty;
public static readonly string StatusProperty;
public static readonly string CountProperty;
public static readonly string DisplayTextProperty;

static Field2Model()
{
    var dummy = new Field2Model(0);

    TimeProperty = GetPropertyName(() => dummy.Time);
    StatusProperty = GetPropertyName(() => dummy.Status);
    CountProperty = GetPropertyName(() => dummy.Count);
    DisplayTextProperty = GetPropertyName(() => dummy.DisplayText);
}

private static string GetPropertyName<T>(Expression<Func<T>> expression)
{
    MemberExpression memberExpression = (MemberExpression)expression.Body;
    return memberExpression.Member.Name;
}
The property implementation is the same as Field
private readonly Field<DateTime> _time = new Field<DateTime>(TimeProperty);
public DateTime Time
{
    get { return _time; }
    set { Set(_time, value); }
}

CallerMemberName

This implementation uses a .Net 4.5+ attribute CallerMemberNameAttribtue.  The implementation simplified all the Setter like implementation by adding a optional parameter and mark the parameter with [CallerMemberName].  The attribute cause the compiler to add the function/property's name as string literal to the method call thus the operation is pretty cheap and has about the same speed as setter.
The setter method look like this
protected bool Set(ref T field, T value, [CallerMemberName]string propertyName = "")
{
    if (field == null || EqualityComparer.Default.Equals(field, value)) { return false; }
    field = value;
    RaisePropertyChanged(propertyName);
    return true;
}
The property implementation is similar to setter without the property name as one of the argument
private DateTime _time;
public DateTime Time
{
    get { return _time; }
    set { Set(ref _time, value); }
}

AOP

The last implementation leverages aspect orientated library such as PostSharp or Fody. PostSharp has some example on how to do it here. I used Fody because it was requested and free.

Test Methodology

A test harness was built to bind the models implementing different property changed strategy. The test application binds a collection of models to a grid and them update the models a million times to generate property changed events. A timer is used to measure program execution time, this is not the CPU time, but the result is sufficient for the comparison. The test harness also create 1 to 100000 objects to account for object construction time, as you will see there are implementations where object construction is somewhat expensive.

Results

The values showing in the chart is in milliseconds and account for total construction time and property update time.


Object Count Simple Setter Lambda Field Lambda Field Field 2 Field With WEH Delegate Setter CMN Attribute Fody
1 752 827 1778 958 1104 970 955 942 814 815
10 730 814 1751 956 1105 959 955 936 810 814
100 733 818 1765 955 1103 961 949 938 810 811
1000 731 842 1885 959 1109 956 956 944 814 820
10000 750 874 2745 984 1205 979 981 1022 870 863
100000 837 999 2837 1122 1950 1095 1123 1171 985 998

The execution time for each implementation is pretty much the same across each implementation regardless of object count, the spike up you see for 100,000 object test is the construction cost.

Conclusion

Simple: use this if you don't mind typing or copy and paste
Setter: good implementation for simpler maintenance
Delegate Setter: good implementation if you are wrapping another class such as entity or dto
Lambda: bad performance not recommended. An interesting point here is the runtime went from 7000+ ms to 1700+ ms when project is convert from .net 4.0 to .net 4.5
Field: preferred method of implementation if you need refactor support in event handler, slightly more boiler plate code compare to setter/CallerMemberName implementation
Lambda Field: use this one if you really really want to use Lambda, but it has a performance penalty if your program create lots of objects. CallerMemberName is preferred over this implementation.
Field 2: recommended method if you just hate literal string for some reason but still want refactor support
CallerMemberName: simple to implement and has good performance, the recommended implementation. However you lose the refactor support in event handler.
APO: no boilerplate code, but your lose refactor support and dependency on external libraries.
You can get the source code to the implementations and test harness at github