Implementing NotifyPropertyChanged without magic strings

Alain picture Alain · Oct 11, 2011 · Viewed 20.1k times · Source

Possible Duplicate:
typesafe NotifyPropertyChanged using linq expressions

I'm working on a large team application which is suffering from heavy use of magic strings in the form of NotifyPropertyChanged("PropertyName"), - the standard implementation when consulting Microsoft. We're also suffering from a great number of misnamed properties (working with an object model for a computation module that has hundreds of stored calculated properties) - all of which are bound to the UI.

My team experiences many bugs related to property name changes leading to incorrect magic strings and breaking bindings. I wish to solve the problem by implementing property changed notifications without using magic strings. The only solutions I've found for .Net 3.5 involve lambda expressions. (for example: Implementing INotifyPropertyChanged - does a better way exist?)

My manager is extremely worried about the performance cost of switching from

set { ... OnPropertyChanged("PropertyName"); }

to

set {  ... OnPropertyChanged(() => PropertyName); }

where the name is extracted from

protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
{
    MemberExpression body = selectorExpression.Body as MemberExpression;
    if (body == null) throw new ArgumentException("The body must be a member expression");
    OnPropertyChanged(body.Member.Name);
}

Consider an application like a spreadsheet where when a parameter changes, approximately a hundred values are recalculated and updated on the UI in real-time. Is making this change so expensive that it will impact the responsiveness of the UI? I can't even justify testing this change right now because it would take about 2 days worth of updating property setters in various projects and classes.

Answer

Alain picture Alain · Oct 12, 2011

I did a thorough test of NotifyPropertyChanged to establish the impact of switching to the lambda expressions.

Here were my test results:

enter image description here

As you can see, using the lambda expression is roughly 5 times slower than the plain hard-coded string property change implementation, but users shouldn't fret, because even then it's capable of pumping out a hundred thousand property changes per second on my not so special work computer. As such, the benefit gained from no longer having to hard-code strings and being able to have one-line setters that take care of all your business far outweighs the performance cost to me.

Test 1 used the standard setter implementation, with a check to see that the property had actually changed:

    public UInt64 TestValue1
    {
        get { return testValue1; }
        set
        {
            if (value != testValue1)
            {
                testValue1 = value;
                InvokePropertyChanged("TestValue1");
            }
        }
    }

Test 2 was very similar, with the addition of a feature allowing the event to track the old value and the new value. Because this features was going to be implicit in my new base setter method, I wanted to see how much of the new overhead was due to that feature:

    public UInt64 TestValue2
    {
        get { return testValue2; }
        set
        {
            if (value != testValue2)
            {
                UInt64 temp = testValue2;
                testValue2 = value;
                InvokePropertyChanged("TestValue2", temp, testValue2);
            }
        }
    }

Test 3 was where the rubber met the road, and I get to show off this new beautiful syntax for performing all observable property actions in one line:

    public UInt64 TestValue3
    {
        get { return testValue3; }
        set { SetNotifyingProperty(() => TestValue3, ref testValue3, value); }
    }

Implementation

In my BindingObjectBase class, which all ViewModels end up inheriting, lies the implementation driving the new feature. I've stripped out the error handling so the meat of the function is clear:

protected void SetNotifyingProperty<T>(Expression<Func<T>> expression, ref T field, T value)
{
    if (field == null || !field.Equals(value))
    {
        T oldValue = field;
        field = value;
        OnPropertyChanged(this, new PropertyChangedExtendedEventArgs<T>(GetPropertyName(expression), oldValue, value));
    }
}
protected string GetPropertyName<T>(Expression<Func<T>> expression)
{
    MemberExpression memberExpression = (MemberExpression)expression.Body;
    return memberExpression.Member.Name;
}

All three methods meet at the OnPropertyChanged routine, which is still the standard:

public virtual void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null)
        handler(sender, e);
}

Bonus

If anyone's curious, the PropertyChangedExtendedEventArgs is something I just came up with to extend the standard PropertyChangedEventArgs, so an instance of the extension can always be in place of the base. It leverages knowledge of the old value when a property is changed using SetNotifyingProperty, and makes this information available to the handler.

public class PropertyChangedExtendedEventArgs<T> : PropertyChangedEventArgs
{
    public virtual T OldValue { get; private set; }
    public virtual T NewValue { get; private set; }

    public PropertyChangedExtendedEventArgs(string propertyName, T oldValue, T newValue)
        : base(propertyName)
    {
        OldValue = oldValue;
        NewValue = newValue;
    }
}