Best Practices: How to implement INotifyPropertyChanged right?

Coordinator
Apr 18, 2009 at 8:52 PM
Edited Jul 27 at 11:27 AM
.
An updated version of this article can be found here: Implementing and usage of INotifyPropertyChanged
.

I believe that the INotifyPropertyChanged interface is one of the most important interfaces the .NET Framework provides. Although, the interface defines just one event it isn’t that easy to implement it right. I have seen various approaches to implement this interface and everyone has its own drawbacks.

I would like to discuss some of the approaches I have seen here. Let’s start with the one I have seen most.

Approach 1: Common implementation
internal class Person1 : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged;  

    private string name;  

    public string Name 
    { 
        get { return name; } 
        set 
        { 
            if (name != value) 
            { 
                name = value; 
                OnPropertyChanged("Name"); 
            } 
       } 
    }  

    protected virtual void OnPropertyChanged(string propertyName) 
    { 
        if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } 
    }
Characteristics
(+) Easy to implement and use.
(+) The event is raised only when the property name has changed. This helps to avoid stack overflows when properties depend on each other.
(-) The property name is defined as string constant inside the code. This is not refactoring save and it is error prone because the compiler doesn’t check if the property name string is correct.
(-) If a property changes frequently and performance is essential then it might be a problem that every property change creates a new EventArgs object.
(-) The OnPropertyChanged method doesn’t follow the Microsoft Design Guidelines (http://msdn.microsoft.com/en-us/library/ms229011.aspx) because this method should have an argument of the type PropertyChangedEventArgs instead of string. One of the reasons for this rule can be seen in Approach 2.

Approach 2: Cached PropertyChangedEventArgs
internal class Person2 : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged;  

    private static readonly PropertyChangedEventArgs NameEventArgs = new PropertyChangedEventArgs("Name");  

    private string name;  

    public string Name 
    { 
        get { return name; } 
        set 
        { 
            if (name != value) 
            { 
                name = value; 
                OnPropertyChanged(NameEventArgs); 
            } 
       } 
    }  

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) 
    { 
        if (PropertyChanged != null) { PropertyChanged(this, e); } } 
    }
Characteristics
(+) The event is raised only when the property name has changed. This helps to avoid stack overflows when properties depend on each other.
(+) The PropertyChangedEventArgs are created once per class and not for every property change. This improves the performance.
(+) The OnPropertyChanged method follows the Microsoft Design Guidelines (http://msdn.microsoft.com/en-us/library/ms229011.aspx). This allows us to cache the PropertyChangedEventArgs to be reused for every property change. Furthermore, a subclass would be able to pass a derived PropertyChangedEventArgs type through this method. By example this derived class might include the old and new value of the property change operation.
(-) The property name is defined as string constant inside the code. This is not refactoring save and it is error prone because the compiler doesn’t check if the property name string is correct.
(-) Every property needs a static field for the PropertyChangedEventArgs.

Approach 3: PropertyName check at runtime
internal class Person3 : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged;  
        
    private string name;  
        
    public string Name 
    { 
        get { return name; } 
        set 
        { 
            if (name != value) 
            { 
                name = value; 
                OnPropertyChanged("Name"); 
            } 
        } 
    }  
        
    protected virtual void OnPropertyChanged(string propertyName) 
    { 
        CheckPropertyName(propertyName); 
        if (PropertyChanged != null) 
        { 
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
        } 
    }  
        
    [Conditional("DEBUG")] 
    private void CheckPropertyName(string propertyName) 
    { 
        PropertyDescriptor propertyDescriptor = TypeDescriptor.GetProperties(this)[propertyName]; 
        if (propertyDescriptor == null) 
        { 
            throw new InvalidOperationException(string.Format(null, "The property with the propertyName '{0}' doesn't exist.", propertyName)); 
        } 
    } 
}
Characteristics
(+) The event is raised only when the property name has changed. This helps to avoid stack overflows when properties depend on each other.
(+) The OnPropertyChanged method checks if the passed property name is valid. This helps a lot to find wrong property names which often results from typos or code refactoring. However, the check costs a lot performance and so it is done only in DEBUG mode.
(-) If a property changes frequently and performance is essential then it might be a problem that every property change creates a new EventArgs object.
(-) The OnPropertyChanged method doesn’t follow the Microsoft Design Guidelines (http://msdn.microsoft.com/en-us/library/ms229011.aspx) because this method should have an argument of the type PropertyChangedEventArgs instead of string. One of the reasons for this rule can be seen in Approach 2.

Approach 4: Retrieve the property name through a lambda expression
internal class Person4 : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged;  
        
    private static readonly string NamePropertyName = TypeManager.GetProperty<Person4>(x => x.Name).Name;  
        
    private string name;  
        
    public string Name 
    { 
        get { return name; } 
        set 
        { 
            if (name != value) 
            { 
                name = value; 
                OnPropertyChanged(NamePropertyName); 
            } 
        } 
    }  
        
    protected virtual void OnPropertyChanged(string propertyName) 
    { 
        if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } 
    } 
} 
Note: The implementation of TypeManager.GetProperty can be seen in the code download.

Characteristics
(+) The event is raised only when the property name has changed. This helps to avoid stack overflows when properties depend on each other.
(+) The TypeManager.GetProperty retrieves the property name through the lambda expression. The lambda expression is type-safe.
(-) The lambda expression uses reflection internally to retrieve the property. This increases the working set (memory usage) of the application and it slows down when the class is accessed the first time.
(-) The usage of the static TypeManager.GetProperty method is not straight-forward.
(-) If a property changes frequently and performance is essential then it might be a problem that every property change creates a new EventArgs object.
(-) The OnPropertyChanged method doesn’t follow the Microsoft Design Guidelines (http://msdn.microsoft.com/en-us/library/ms229011.aspx) because this method should have an argument of the type PropertyChangedEventArgs instead of string. One of the reasons for this rule can be seen in Approach 2.

Approach 5: Do not use the StackTrace
// DO NOT USE THIS! 
protected void OnPropertyChanged() 
{ 
    StackTrace s = new StackTrace(1, false); 
    string propertyName = s.GetFrame(0).GetMethod().Name.Substring(4); 
    if (PropertyChanged != null) 
    {  
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));  
    } 
}  
I have also seen implementations that use the StackTrace class to retrieve the property name. Unfortunately, this doesn’t work correct for various reasons: The StackTrace implementation relies on the debug symbols. By default, Debug builds include debug symbols, while Release builds do not. Thus, the application works in Debug mode but doesn’t work in Release mode anymore.
The StackTrace isn’t designed to be called frequently. The performance would be low if it is used for the property changed implementation.

Code Download: http://compositeextensions.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=26312

More: How to implement INotifyPropertyChanged right? (Part 2): Calculated Properties and Unit Testing
Sep 5, 2009 at 7:58 PM
Edited Sep 5, 2009 at 8:10 PM

There's an additional approach which I use, which is a combination of 2 and 4 - cache a PropertyChangedEventArgs object in a static variable.  Use a helper function which uses expression syntax to return a created event args object.  Then make use of the cached event args object for change notifications.

Example usage:

 

class Person6 : INotifyPropertyChanged
{
    static readonly PropertyChangedEventArg _nameChangedArgs = ObservableHelper.CreateArgs<Person6>(x => x.Name); 
    string _name;

    public string Name
    {
        get { return _name; }
        set
        {
            if (_name != value)
            {
                _name = value;
                OnPropertyChanged(_nameChangedArgs);
            }
        }
    }

    protected void OnPropertyChanged(PropertyChangedEventArgs propertyChangeArgs)
    {
        var changeEvent = PropertyChanged;

        if (changeEvent != null)
        {
            changeEvent(this, propertyChangeArgs);
        }
    }

   public event PropertyChangedEventHandler PropertyChanged;
}

 

This of course does take the performance hit the first time the type is used.  Personally I don't see that as an issue - after all, it's a one time hit.  And, it has the advantage as you stated for 2) that its marginally quicker on the change notification itself.

Regards,

Phil

EDIT: BTW, on the base class I have for implementation of INotifyPropertyChanged, I do *not* provide an 'OnPropertyChanged' method which takes a string argument - the reason is I want to actively disuade users from passing in hardcoded strings, and push them towards the use of my provided helper method and expression syntax so that their code is refactoring and obfuscation safe.  There is nothing at the end of the day to stop them from newing up an event args object if they really want to, but I explain in the OnPropertyChanged method xml comments that they should use the CreateArgs helper method and expression syntax.

There is an additional method I provide in the base class named 'OnAllPropertiesChanged()' which transmits a property changed event using a cached property changed event args object, which was create with string.empty.  This supports the convention for INotifyPropertyChanged where a null or empty string can be used for the property change args in order to indicate that the recepient should assume all properties have changed (and if binding requery these properties for their current value).

 

May 3, 2010 at 9:08 AM
Edited May 3, 2010 at 9:13 AM

Here is a typesafe implementation using expression trees:

public void NotifyOfPropertyChange<TProperty>(Expression<Func<TProperty>> property)
{
   
var lambda = (LambdaExpression)property;
   
MemberExpression memberExpression;
   
if (lambda.Body is UnaryExpression)
   
{
       
var unaryExpression = (UnaryExpression)lambda.Body;
        memberExpression
= (MemberExpression)unaryExpression.Operand;
   
}
   
else memberExpression = (MemberExpression)lambda.Body;
   
NotifyOfPropertyChange(memberExpression.Member.Name);
 
}
It can be used like this:

public string Name
{
    get { return _name; }
    set
    {
        if (_name != value)
        {
           _name = value;
NotifyOfPropertyChange
(() => Name);

        }
    }
}

Dec 30, 2010 at 4:26 AM

Nice summary of the options. 

I have documented three more in a CodePlex project dedicated to the subject, ActiveSharp.Codeplex.com.

Briefly, they are:

  • Always send string.Empty as the property name, which means "all my properties have changed".  Depending on the situation, this may or may not suit.
  • Hard-code the property name as a string, but write unit tests which check the name.  (Arguably a better option that a runtime check that only happens when a property is used).
  • Use the full functionality of ActiveSharp, to identify properties at runtime without hard coded strings, without lambda expressions, and without a stack crawl.
Feb 17, 2011 at 12:09 AM

If you're using Unity and don't mind your class inheriting from MarshalByRefObject, then Dmitry Schechtman has a nice bit of AOP using Unity Interception that will do it automagically for you - in fact, it does it to the extent that you can use auto-properties again, so your class above would like this:

[NotifyPropertyChanged]
class Person6 : MarshalByRefObject, INotifyPropertyChanged
{
    public string Name { get; set; }
    public event PropertyChangedEventHandler PropertyChanged;
}
I don't know about you, but that is pretty sweet!

Mar 26, 2011 at 9:34 PM
Edited Mar 26, 2011 at 9:34 PM

Stack walk is slow and lambda expression is even slower. We have solution similar to well known lambda expression but almost as fast as string literal. See
http://zamboch.blogspot.com/2011/03/raising-property-changed-fast-and-safe.html

Mar 26, 2011 at 11:51 PM

pavel, I've made a posting to your blog.  Unfortunately this method won't work.  I came up with the same idea, but unfortunately it's not refactoring safe.

 

Regards,

Phil