Reprint - MVVM Lambda vs INotifyPropertyChanged vs DependencyObject

THis page is a reprint of the document at http://blog.quantumbitdesigns.com/2010/01/26/mvvm-lambda-vs-inotifypropertychanged-vs-dependencyobject/, under the same title of "MVVM - Lambad vs INotifyPropertyChanged vs DependencyObject"

 

Before start, many developers are very confused about the INPC (INotifyPropertyChanged vs  DependencyObject ) changed, there is a discussion which lead to the following article.

 

https://channel9.msdn.com/Forums/TechOff/466194-ViewModel--INotifyPropertyChanged-or-DependencyObject

 

and the conclusion being " INPC is is what ought to be used in most cases ..."

 

 

 

MVVM – Lambda vs INotifyPropertyChanged vs DependencyObject

It has been about 1.5 years since I last posted. When the market crashed I became fascinated by world of trading (and software) and applied a bit of WPF knowledge to some research and software for another blog. I have 20+ topics still pending for this blog, so there is plenty to write about in 2010 and beyond.

I have been using the MVVM pattern since the beginning of 2008. Since that time there have been plenty of complaints about INotifyPropertyChanged and how developers do not like using strings, etc. Last year several bloggers presented type-safe alternatives that used either reflection or lambda expressions. I really like the elegance and simplicity of the lambda expression, but many are quick to point out the poor performance (same for reflection). Unfortunately ‘poor performance’ says little about usefulness of an implementation. The performance needs to be quantified and compared against all implementations so that ‘poor’ can be put in perspective. Speed is not the only issue, as memory should be compared as well.

This article assumes the reader knows how to use INotifyPropertyChanged and DependencyObjects. (Note: the rest of this article will refer to the string implementation as INotifyPropertyChanged or just INotify). The lambda implementation that is used for the tests is as follows:

INotifyPropertyChanged with Lambda

public static class MvvmExtensions

{

    public static void Raise(this PropertyChangedEventHandler handler, object sender, Expression<Func<object>> expression)

    {

        if (handler != null)

        {

            if (expression.NodeType != ExpressionType.Lambda)

            {

                throw new ArgumentException(“Value must be a lamda expression”, “expression”);

            }

            var body = expression.Body as MemberExpression;

            if (body == null)

            {

                throw new ArgumentException(“‘x’ should be a member expression”);

            }

            string propertyName = body.Member.Name;

            handler(sender, new PropertyChangedEventArgs(propertyName));

        }

    }

}

public class DummyLambdaViewModel : INotifyPropertyChanged

{

    private string _dummyProperty;

    public string DummyProperty

    {

        get

        {

            return this._dummyProperty;

        }

        set

        {

            this._dummyProperty = value;

            OnPropertyChanged(() => this.DummyProperty);

        }

    }

    protected virtual void OnPropertyChanged(Expression<Func<object>> expression)

    {

        this.PropertyChanged.Raise(this, expression);

    }

    public event PropertyChangedEventHandler PropertyChanged;

}

Since many developers like to use a base view model class, if the property changed method is put in the base class then children will easily get the support for the lambda expression:

public abstract class ViewModelBase : INotifyPropertyChanged

{

    public ViewModelBase()

    {

    }

    protected virtual void OnPropertyChanged(Expression<Func<object>> expression)

    {

        this.PropertyChanged.Raise(this, expression);

    }

    public event PropertyChangedEventHandler PropertyChanged;

}

The derived class is now a bit more clean.

public class DummyLambdaViewModel : ViewModelBase

{

    private string _dummyProperty;

    public string DummyProperty

    {

        get

        {

            return this._dummyProperty;

        }

        set

        {

            this._dummyProperty = value;

            OnPropertyChanged(() => this.DummyProperty);

        }

    }

}

References
The lambda expression code was taken from the following blogs
http://www.jphamilton.net/post/MVVM-with-Type-Safe-INotifyPropertyChanged.aspx
http://blog.decarufel.net/2009/07/how-to-use-inotifypropertychanged-type_22.html
http://consultingblogs.emc.com/merrickchaffer/archive/2010/01/22/a-simple-implementation-of-inotifypropertychanged.aspx

Testing without WPF Bindings
There are essentially two components to lambda property change notification that cost performance. The first is the creation of the expression (such as () => this.DummyProperty). The second is the actual WPF binding infrastructure that affects all of the property change notification implementations. String property change events have virtually no ‘expression creation’ processing time since it is just a hard coded string (such as “DummyProperty”). By raising property change events when no bindings are attached (and thus no listener to the viewmodel’s PropertyChanged event), we can compare the ‘expression creation’ performance costs of the various property change implementations.

Reprint - MVVM Lambda vs INotifyPropertyChanged vs DependencyObject

The lambda expressions are roughly 20 times slower than both string INotify and DependencyObjects when the WPF binding infrastructure is not included, giving us a measure of the raw performance cost of just raising the property change event. So far the performance cost of lambda expressions is living up to the ‘poor performance’ label.

Testing with WPF Bindings
Since the prior test is only part of the total processing required for property change notification, it is important to test again with a WPF binding in place so that the WPF binding system can work its magic against the property change events. By having a XAML element (such as a TextBlock) bind its Text property to a property of a viewmodel, WPF’s binding system will now be listening for the property change events and doing its own reflection and internal property management.

Reprint - MVVM Lambda vs INotifyPropertyChanged vs DependencyObject

Once the WPF binding system is included in the performance tests, the lambda expression implementation drops from 20 times slower to only around 3 times slower. Even though using lambda expressions is slower than the alternatives, testing with the WPF binding system shows the performance hit is probably not as big as many have perceived it to be. Using  property change notification on a DependencyObject is the fastest technique since it was designed for that purpose, allowing the WPF binding system to listen to property change events with virtually no overhead and no reflection. However, like the INotify implementation, it uses a string for the property name identification.

Memory Usage
Performance is only half the story. In order to make proper design decisions, it is important to also know the memory characteristics of the various property change techniques. By using a profiler we can measure the number of objects created and destroyed, as well as bytes used during each property change request. The WPF binding system is included in the measurements.

Reprint - MVVM Lambda vs INotifyPropertyChanged vs DependencyObject

The lambda expressions use almost 7 times the memory as INotify for each property change event, while the DependencyObject property change notification uses no memory at all (but the class itself uses memory). The following screenshot from the profiler shows the WPF binding system objects that are created and destroyed during the INotify event (1,000,000 events):

Reprint - MVVM Lambda vs INotifyPropertyChanged vs DependencyObject

These are the objects and memory used for the lambda expression (1,000,000 events)

Reprint - MVVM Lambda vs INotifyPropertyChanged vs DependencyObject

General Guidelines
With this performance and memory usage information we can summarize some loose guidelines for when to use each property change mechanism. Since the DispatcherObject is a WPF class, it has its own set of memory requirements to support

  • DispatcherObject: Use if speed critical and frequent property change events occur
  • INotifyPropertyChanged: Use if large numbers of viewmodels are created (it is more lightweight than DependencyObject), but speed is also important
  • Lambda Expressions: Use for type-safe property change notification when speed is not critical.

Test Application
You can download the application and code used for these tests here. The code is junk code, and is not a demonstration of proper design, testing, or MVVM principles.

Reprint - MVVM Lambda vs INotifyPropertyChanged vs DependencyObject

Disclaimer
These tests were performed using a viewmodel with only one property. Performance can differ on viewmodels with many properties. Tests were also performed by raising the property change notifications in a loop. Real world performance can vary due to locality of reference.

相关推荐