Safe implementation of INotifyPropertyChanged interface

With arrival of WPF the usage of INotifyPropertyChanged interface has expanded dramatically. The most annoying and dangerous part of implementing the interface is, obviously the fact, that you have to pass your option names as strings. This leaves your code prone to bugs - if in 3 months time you rename one of the properties, there is very good chance that you will forget to update the string name as well. In order to overcome the problem we can use some reflection and a little bit of boiler plate code. The good people of the Internet have done all the work for us already, so just copy, paste and enjoy!

Additional advantages of the technique, presented below is better performance, since the parameter objects for property changed events are allocated statically only once. The only drawback is slight increase in the size of static payload size of the class, but not the size of an individual instance. The manual effort you need to put into generating the code cannot be ignored, but is not that large. Aspect oriented programming, such as PostSharp might be a solution to this problem, but it is beyond the scope of this article.

The code below is based on this discussion

First of all add the class below to your project:

  1.     public static class TypeManager
  2.     {
  3.         [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
  4.         public static PropertyInfo GetProperty<TType>(Expression<Func<TType, object>> propertySelector)
  5.         {
  6.             Expression expression = propertySelector.Body;
  7.  
  8.             // If the Property returns a ValueType then a Convert is required => Remove it
  9.             if (expression.NodeType == ExpressionType.Convert || expression.NodeType == ExpressionType.ConvertChecked)
  10.             {
  11.                 expression = ((UnaryExpression)expression).Operand;
  12.             }
  13.  
  14.             // If this isn't a member access expression then the expression isn't valid
  15.             MemberExpression memberExpression = expression as MemberExpression;
  16.             if (memberExpression == null)
  17.             {
  18.                 ThrowExpressionArgumentException("propertySelector");
  19.             }
  20.  
  21.             expression = memberExpression.Expression;
  22.  
  23.             // If the Property returns a ValueType then a Convert is required => Remove it
  24.             if (expression.NodeType == ExpressionType.Convert || expression.NodeType == ExpressionType.ConvertChecked)
  25.             {
  26.                 expression = ((UnaryExpression)expression).Operand;
  27.             }
  28.  
  29.             // Check if the expression is the parameter itself
  30.             if (expression.NodeType != ExpressionType.Parameter)
  31.             {
  32.                 ThrowExpressionArgumentException("propertySelector");
  33.             }
  34.  
  35.             // Finally retrieve the MemberInfo
  36.             PropertyInfo propertyInfo = memberExpression.Member as PropertyInfo;
  37.             if (propertyInfo == null)
  38.             {
  39.                 ThrowExpressionArgumentException("propertySelector");
  40.             }
  41.  
  42.             return propertyInfo;
  43.         }
  44.  
  45.         private static void ThrowExpressionArgumentException(string argumentName)
  46.         {
  47.             throw new ArgumentException("It's just the simple expression 'x => x.Property' allowed.", argumentName);
  48.         }
  49.  
  50.         public static PropertyChangedEventArgs GetPropertyChangedEventArgs<TType>(Expression<Func<TType, object>> propertySelector)
  51.  
  52.         {
  53.             return new PropertyChangedEventArgs(GetProperty(propertySelector).Name);
  54.         }
  55.     }

Next, when you want to implement the INotifyPropertyChanged interface, you should follow this pattern (read this article for the explanation regarding some attributes in the code):

  1.     public class Test : INotifyPropertyChanged 
  2.     {
  3.         [NonSerialized]
  4.         private static readonly PropertyChangedEventArgs _valuePropertyChangedArgs =
  5.             TypeManager.GetPropertyChangedEventArgs<Test>(x => x.Value);
  6.  
  7.         private string _value:
  8.  
  9.         public string Value
  10.         {
  11.             get
  12.             {
  13.                 return _value;
  14.             }
  15.             set
  16.             {
  17.                 if (_value != value)
  18.                 {
  19.                     _value = value;
  20.                     OnPropertyChanged(_valuePropertyChangedArgs);
  21.                 }
  22.             }
  23.         }
  24.  
  25.         #region INotifyPropertyChanged Members
  26.  
  27.         [field: NonSerialized]
  28.         public event PropertyChangedEventHandler PropertyChanged;
  29.  
  30.         #endregion
  31.  
  32.         private void OnPropertyChanged(PropertyChangedEventArgs propertyChangeArgs)
  33.         {
  34.             PropertyChangedEventHandler h = PropertyChanged;
  35.             if (h != null)
  36.             {
  37.                 h(this, propertyChangeArgs);
  38.             }
  39.         }
  40.     }
Topic: 

Add new comment