Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / C#

Performance and Ideas from Anders Hejlsberg INotifyPropertyChanged

Rate me:
Please Sign up or sign in to vote.
4.94/5 (26 votes)
22 Feb 2012CPOL7 min read 81.1K   292   46   72
Performance and ideas from Anders Hejlsberg INotifyPropertyChanged

Overview

I have never been really thrilled with having to write code to implement the INotifyPropertyChanged interface. Part of the reason is that it does not feel right having to use strings when triggering the PropertyChanged event, and then there is all the code that is required. Originally there had to be a backing field for all properties, and then Microsoft made a big step forward and made properties much simpler by not requiring the defining of a backing field. With WPF, it was two (maybe more) steps back. Anything to simplify simple properties for WPF/Silverlight binding is a step forward. The best option possible would be to call a method in the property that implements the required code, but this requires some tricks.

Implementation of INotifyPropertyChanged

The MSDN recommended implementation for INotifyPropertyChanged (How to: Implement the INotifyPropertyChanged Interface) is similar to the following:

C#
class MsdnVersionExample : INotifyPropertyChanged
{
  private int _variable;

  public int Variable
  {
    get { return this._variable; }
    set
    {
      if (value != this._variable)
      {
        this._variable = value;
        NotifyPropertyChanged("Variable");
      }
    }
  }
  private void NotifyPropertyChanged(String propertyName)
  {
    if (PropertyChanged != null)
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  }

  public event PropertyChangedEventHandler PropertyChanged;
}

This code implements most of what should be in the property used for binding but it is a lot of code and this has to be done for each property, and it is actually missing a feature it should have.

In Anders Hejlsberg talk Future directions for C# and Visual Basic, I noticed that he did something very different:

C#
class AndersVersionExample : INotifyPropertyChanged
{
  private int _variable;

  public int Variable
  {
    get { return this._variable; }
    set
    {
        SetProperty(ref _variable, value, "Variable");
    }
  }

  private void SetProperty<T>(ref T field, T value, string propertyName)
  {
    if (!EqualityComparer<T>.Default.Equals(field, value))
    {
      field = value;
      var handler = PropertyChanged;
      if (handler != null)
      {
        handler(this, new PropertyChangedEventArgs(propertyName));
      }
    }
  }

  public event PropertyChangedEventHandler PropertyChanged;
}

This does a great job of cleaning up the actual property itself, meaning that everything is cleaner. What allows this to work is the EquityComparer<T>.Default, which exposes the Equals method. Also note the actual handling of the event where there a “handler” used to save the event information before firing an event (which can prevent a race condition) is internally the Microsoft preferred method.

Now looking that the Ander’s implementation and the MSDN implementation, it can immediately be seen that the MSDN is missing the assigning of the PropertyChanged to a variable before firing the event. This is to prevent a race condition, and is internally preferred practice within Microsoft Corporation. Many people will even use the simpler implementation than the MSDN, which is not to check if the value has been changed before changing the value and firing the PropertyChanged event. Thus the code that should be implemented for each simple property for binding is quite a bit, and repeating the code becomes a maintenance issue, and all this extra code clouds the understanding of what the application is doing.

In my group, we have been using something different, which has the advantage of eliminating the magic string, which is the name of the property:

C#
class OurVersionExample : INotifyPropertyChanged
{
  private int _variable;

  public int Variable
  {
    get { return this._variable; }
    set
    {
      if (value != this._variable)
      {
        this._variable = value;
        NotifyPropertyChanged(GetPropertyName(() => Variable));
      }
    }
  }

  public static string GetPropertyName<T>(Expression<Func<T>> property)
  {
      var memberExpression = (MemberExpression)((lambda.Body is UnaryExpression)
             ? ((UnaryExpression)lambda.Body).Operand : lambda.Body);
    Debug.Assert(memberExpression != null);
    return memberExpression.Member.Name;
  }

  private void NotifyPropertyChanged(String propertyName)
  {
    if (PropertyChanged != null)
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  }

  public event PropertyChangedEventHandler PropertyChanged;
}

Now this is very nice since when the variable name is changed using refactoring, the value used in the property changed is also changed, however, there is going to be a performance impact, which I shall get into later. (I did make some changes from what we were originally using, inserting the Debug.Assert instead of doing a check since this will not be in the production code, and there should not be a case where memberExpression is null unless there is a fairly obvious error in the property code. I also cleaned up a couple of other issues.)

If the “Expression<Func<T>>” is confusing, I have to agree it is. What the Expression does is that it gives access to the anonymous method. This is used because it provides information about the lambda expression, which would not be available if there was just the lambda expression in the argument. It is possible to create the lambda expression using the compile statement (in this case would compile to Func<T>):

C#
Func<T> _lambda = `_expression.Compile ();

Then this function (Func<T>) could just be executed:

C#
var value = _lambda();

I believe that the “UnaryExpression” is to handle the case the programmer places parenthesis around the variable, so could be dropped. This is not code that I wrote, and know that there have been other cases where there have been issues.

Now I like what Anders did, and I like the way our group does it. I do know that the actual handling of the event where there a “handler” is used to save the event information before firing an event (which can prevent a race condition) is the preferred method, why not combine the two:

C#
class ModifiedAndersVersionExample : INotifyPropertyChanged
{
  private int _variable;

  public int Variable
  {
    get { return this._variable; }
    set
    {
      SetProperty(ref _variable, value, () => Variable);
    }
  }

  private void SetProperty<T>(ref T field, T value, Expression<Func<T>> property)
  {
    if (!EqualityComparer<T>.Default.Equals(field, value))
    {
      field = value;
      NotifyPropertyChanged (property);
    }
  }

  public void NotifyPropertyChanged<T>(Expression<Func<T>> property)
  {
    var handler = PropertyChanged;
    if (handler != null)
    {
      var memberExpression = (MemberExpression)((lambda.Body is UnaryExpression)
             ? ((UnaryExpression)lambda.Body).Operand : lambda.Body);
      Debug.Assert(memberExpression != null);
      handler(this, new PropertyChangedEventArgs(memberExpression.Member.Name));
    }
  }

  public event PropertyChangedEventHandler PropertyChanged;
}

You will notice that I still have the NotifyPropertyChanged method and also Ander’s SetProperty method, however the SetProperty method now calls the NotifyPropertyChanged method. I use two methods because there are many times that the PropertyChanged event needs to be fired, but the value is already correct (could be that some equation or a Linq statement is used to determine the right value).

A final test also includes a call to MethodBase.GetCurrentMethod() to get the property name so that the straight Anders' method can be used (This test was added because of a comment from Hanzie53). The performance actually looks good compared to the lambda methods, but what cannot be seen in the results is the initialization cost. It has two disadvantages: it makes the call more complex, and MethodBase.GetCurrentMethod() cannot be used outside of the property. The actual test is a little bastardized because I am not calling MethodBase.GetCurrentMethod() from a property.

Another comment lead me to also test saving the name in a dictionary, but the cost the checking and lookup appeared to have similar performance cost, and I did not think it was worth leaving in the test.

Results

Obviously making the properties simpler using a call to a method has the advantages of code reuse, but at what cost. Ander’s method does not appear to have too much of a performance hit, but how much, and how much penalty will there be passing a lambda expression which will allow the name of the property to be determined.

The application included with this article is basically a test of the four implementations for INotifyPropertyChanged:

  1. Basic MSDN implementation
  2. The implementation my group has been using
  3. The implementation from the Anders Hejlsberg talk
  4. The implementation combining the Anders’ version and the one we have been using
  5. The implementation combining the Anders’ version with MethodBase.GetCurrentMethod()

I do several runs, which include:

  1. Initial run, which will get everything complied and in memory (Note: There is a significant difference when running 1000 iterations instead of 10,000)
  2. A second run where everything should be compiled and in memory
  3. A run where an empty event handler is triggered
  4. A run where there is binding to the Text property of a TextBlock on a WPF form.

A sample run produces the following results:

First Run

Count of number of iterations: 10000

Time for MSDN Implementation = 581 ticks
Time for our current implementation = 119532 ticks
Time for Anders implementation = 815 ticks
Time for combined implementation = 117971 ticks
Time for combined implementation = 119639 ticks
Time using reflection GetCurrentMethod = 61407 ticks


Second Run

Count of number of iterations: 10000

Time for MSDN Implementation = 560 ticks
Time for our current implementation = 116366 ticks
Time for Anders implementation = 1334 ticks
Time for combined implementation = 120099 ticks
Time for combined implementation = 119186 ticks
Time using reflection GetCurrentMethod = 61814 ticks


Run with Property Changed event

Count of number of iterations: 10000

Time for MSDN Implementation = 888 ticks
Time for our current implementation = 117229 ticks
Time for Anders implementation = 1211 ticks
Time for combined implementation = 129511 ticks
Time for combined implementation = 123790 ticks
Time using reflection GetCurrentMethod = 61666 ticks


Run with Binding

Count of number of iterations: 10000

Time for MSDN Implementation = 183643 ticks
Time for our current implementation = 427613 ticks
Time for Anders implementation = 190121 ticks
Time for combined implementation = 427392 ticks
Time for combined implementation = 425799 ticks
Time using reflection GetCurrentMethod = 346575 ticks

As can be seen from the results, the impact of using implementation from Anders’ talk is minimal; significantly less than having to handle an empty event. Doing the determination of the Name using a lambda expression has a very high overhead, on the order of the cost for binding. Using the MethodBase.GetCurrentMethod() to get the name gives a significant performance improvement over using a lambda expression. (Note: This is not a realistic situation, and it may be that the real cost of binding could be much higher since the framework could be eliminating redundant events). The image below is the running of the example included with this article, only run with only 1000 iterations instead of 10,000. It can be seen that the time required for 1000 iterations it is bit more than 1/10th the time of 10,000.

331274/image001.png

Implementation

My group does not actually put this INotifyPropertyChanged code in every ViewModel, instead there is an abstract class that contains the INotifyPropertyChanged code, and all ViewModels inherit from this abstract class.

C# 5.0

smolesen pointed out that C# 5.0 may give us another approach using Caller Info Attributes. This will allow the Anders' implementation to be modified as follows:

C#
class AndersVersionExample : INotifyPropertyChanged
{
  private int _variable;

  public int Variable
  {
    get { return this._variable; }
    set
    {
        SetProperty(ref _variable, value);
    }
  }

  private void SetProperty<T>(ref T field, T value,
      [CallerMemberName] string propertyName = "")
  {
    if (!EqualityComparer<t />.Default.Equals(field, value))
    {
      field = value;
      var handler = PropertyChanged;
      if (handler != null)
      {
        handler(this, new PropertyChangedEventArgs(propertyName));
      }
    }
  }

  public event PropertyChangedEventHandler PropertyChanged;
}

Conclusion

After my investigation, we are definitely going to apply the Anders’ ideas into our code, and we are also going to investigate whether elimination of the determination of the property name from a lambda expression will give us an appreciable performance gain—my initial impression is that the maintenance improvement of using the lambda expression will be worth the performance penalty. However, in a grid with many controls, it may be that there will be an appreciable performance gain. Therefore, we will probably use the Anders’ implementation with and without the lambda expression.

Maybe with Rosyln properties used for binding can be made simpler, with just an attribute, in the mean time we can learn from the implementation in Anders’ talk.

Do look at some of the responses I got from other users because they may include ideas that you may prefer over what I have presented in the article. I may not have included them in the article because I did not think that they was a significant improvement in maintainability from what I presented, however opinions differ and the approaches do have merit.

History

  • 15th February, 2012: Initial version
  • 21st February, 2012: Update includes changes due to comments from other members, and some code fixes

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Clifford Nelson Consulting
United States United States
Has been working as a C# developer on contract for the last several years, including 3 years at Microsoft. Previously worked with Visual Basic and Microsoft Access VBA, and have developed code for Word, Excel and Outlook. Started working with WPF in 2007 when part of the Microsoft WPF team. For the last eight years has been working primarily as a senior WPF/C# and Silverlight/C# developer. Currently working as WPF developer with BioNano Genomics in San Diego, CA redesigning their UI for their camera system. he can be reached at qck1@hotmail.com.

Comments and Discussions

 
QuestionRemoving the field from the view model Pin
FantasticFiasco26-Feb-12 23:18
FantasticFiasco26-Feb-12 23:18 
GeneralRe: Removing the field from the view model Pin
FantasticFiasco20-Mar-12 20:49
FantasticFiasco20-Mar-12 20:49 
GeneralRe: Removing the field from the view model Pin
owen65432121-Mar-12 5:03
owen65432121-Mar-12 5:03 
GeneralRe: Removing the field from the view model Pin
FantasticFiasco21-Mar-12 5:16
FantasticFiasco21-Mar-12 5:16 
GeneralRe: Removing the field from the view model Pin
FantasticFiasco21-Mar-12 5:19
FantasticFiasco21-Mar-12 5:19 
QuestionWhy not use the best of both worlds? Pin
FZelle23-Feb-12 1:38
FZelle23-Feb-12 1:38 
AnswerRe: Why not use the best of both worlds? Pin
Clifford Nelson23-Feb-12 11:06
Clifford Nelson23-Feb-12 11:06 
GeneralMy vote of 5 Pin
hoernchenmeister23-Feb-12 0:42
hoernchenmeister23-Feb-12 0:42 
AnswerRe: My vote of 5 Pin
Clifford Nelson23-Feb-12 5:00
Clifford Nelson23-Feb-12 5:00 
GeneralMy vote of 4 Pin
Armando de la Torre22-Feb-12 6:56
Armando de la Torre22-Feb-12 6:56 
AnswerRe: My vote of 4 Pin
Clifford Nelson22-Feb-12 9:11
Clifford Nelson22-Feb-12 9:11 
QuestionPerformance with lambda caching Pin
pbalaga21-Feb-12 2:23
pbalaga21-Feb-12 2:23 
AnswerRe: Performance with lambda caching Pin
MR_SAM_PIPER21-Feb-12 12:22
MR_SAM_PIPER21-Feb-12 12:22 
GeneralRe: Performance with lambda caching Pin
pbalaga22-Feb-12 2:08
pbalaga22-Feb-12 2:08 
QuestionRe: Performance with lambda caching Pin
User 246299122-Feb-12 5:53
professionalUser 246299122-Feb-12 5:53 
AnswerRe: Performance with lambda caching Pin
Clifford Nelson22-Feb-12 9:12
Clifford Nelson22-Feb-12 9:12 
GeneralRe: Performance with lambda caching Pin
MR_SAM_PIPER22-Feb-12 11:30
MR_SAM_PIPER22-Feb-12 11:30 
GeneralRe: Performance with lambda caching Pin
pbalaga22-Feb-12 22:28
pbalaga22-Feb-12 22:28 
SuggestionRe: Performance with lambda caching Pin
Clifford Nelson23-Feb-12 4:58
Clifford Nelson23-Feb-12 4:58 
AnswerRe: Performance with lambda caching Pin
Clifford Nelson23-Feb-12 5:03
Clifford Nelson23-Feb-12 5:03 
AnswerRe: Performance with lambda caching Pin
Clifford Nelson23-Feb-12 4:57
Clifford Nelson23-Feb-12 4:57 
GeneralRe: Performance with lambda caching Pin
pbalaga23-Feb-12 5:33
pbalaga23-Feb-12 5:33 
AnswerRe: Performance with lambda caching Pin
Clifford Nelson23-Feb-12 5:57
Clifford Nelson23-Feb-12 5:57 
GeneralRe: Performance with lambda caching Pin
pbalaga23-Feb-12 7:35
pbalaga23-Feb-12 7:35 
Initially I was talking about property.GetHashCode() where property is your Expression<Func<T>> method parameter. This is also wrong though, because I mixed up some facts. To be frank, at the moment I don't really understand how a dictionary approach should work in this application, unless you'd pass the same expression instance to the SetParameter3 method (whenever a certain property notifies changes) and use it as a dictionary key. To be able to do so you'd have to store () => BackingProperty expressions as fields, which brings us to the starting point and is useless.
AnswerRe: Performance with lambda caching Pin
Clifford Nelson23-Feb-12 8:40
Clifford Nelson23-Feb-12 8:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.