Building refactoring-friendly observable objects in C#


INotifyPropertyChanged is one the most frequently used .NET interfaces nowadays. I use this interface every day within the models in all kinds of applications: Windows Forms, WPF, Silverlight.

However there is one disadvantage in using INotifyPropertyChanged I have to deal with very often – it is not “refactoring-friendly” and requires a lot of efforts when supporting large scalable enterprise applications/frameworks.

Here’s a skeleton of a common observable object exposing INotifyPropertyChanged interface we all usually deal with:

public class MyCommonObject : INotifyPropertyChanged
{
  #region INotifyPropertyChanged Members

  public event PropertyChangedEventHandler PropertyChanged;

  protected void OnPropertyChanged(string propertyName)
  {
    var handler = PropertyChanged;
    if (handler != null) 
      handler(this, new PropertyChangedEventArgs(propertyName));
  }

  #endregion
}

 

Now let’s take a “Name” property that supports value change notifications:

public class MyCommonObject : INotifyPropertyChanged
{
  private string _Name;

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

  #region INotifyPropertyChanged Members  
  #endregion
}

 

When you want to rename “Name” property above to something like “FirstName” using some refactoring tool the notification will work properly only when the “OnPropertyChanged” call argument is changed as well. It is quite easy to change the code above to call OnPropertyChanged(“FirstName”) but it’s not so easy when you deal with hundreds of objects with possibly hundreds of properties spread over more than one project/solution. Sometimes it is not only the property who notifies about value changes and this makes the refactoring/support task extremely difficult and error-prone.

Being tired of searching broken “OnPropertyChanged” occurrences I’ve started to think over modifying this pattern in order to make it “refactoring-aware”. I wanted to pass something like a “pointer” to the property rather than it’s name so that each time the property name is changed the “pointer” does not require additional tuning. After trying out several approaches I finally found the one that perfectly suits my needs.

Linq Expressions to the rescue…

I’ve decided to reuse Linq Expressions and lambda Functions in order to pass a strongly-typed reference to the object’s property for the “OnPropertyChanged” method. I ended with the following modification of basic “INotifyPropertyChanged” implementation:

public class ObservableObject<T> : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;

  protected virtual void OnPropertyChanged(Expression<Func<T, object>> property)
  {
    if (property == null || property.Body == null) return;

    var memberExp = property.Body as MemberExpression;
    if (memberExp == null) return;
    
    var handler = PropertyChanged;
    if (handler != null)
      handler(this, new PropertyChangedEventArgs(memberExp.Member.Name));
  }

As you can see “OnPropertyChanged” method was changed to receive Expression<Func<T, object>> instead of a string-based property name.

You don’t need compiling or executing this expression. The primary goal is accessing its Body that is represented by a MemberExpression. MemberExpression instance wraps a common “System.Reflection.MemberInfo” as a Member property and so it is obvious that you can get the actual name of the property based on reflection and create a proper event arguments on the fly.

Note: This is a basic implementation and does not consider various possible scenarios. I simply exit method execution if things go the unexpected way. You may want providing some exceptions or error handling.

As you may have noticed I’ve provided a generic class implementation that can be consumed by my objects. Here’s a basic sample of the object consuming modified pattern:

public class MyObject : ObservableObject<MyObject>
{
  private bool _BoolProperty;
  public bool BoolProperty
  {
    get { return _BoolProperty; }
    set
    {
      if (_BoolProperty == value) return;
      _BoolProperty = value;
      OnPropertyChanged(@this => @this.BoolProperty);
    }
  }

  private int _IntProperty;
  public int IntProperty
  {
    get { return _IntProperty; }
    set
    {
      if (_IntProperty == value) return;
      _IntProperty = value;
      OnPropertyChanged(@this => @this.IntProperty);
    }
  }

  private string _StringProperty;
  public string StringProperty
  {
    get { return _StringProperty; }
    set
    {
      if (_StringProperty == value) return;
      _StringProperty = value;
      OnPropertyChanged(@this => @this.StringProperty);
    }
  }

  private object _ObjectProperty;
  public object ObjectProperty
  {
    get { return _ObjectProperty; }
    set
    {
      if (_ObjectProperty == value) return;
      _ObjectProperty = value;
      OnPropertyChanged(@this => @this.ObjectProperty);
    }
  }
}

 

I guess the sample above does not need detailed comments. Instead of string-based property name you now pass the property. If you or someone else decides refactoring the name of the property all notification logic will be automatically updated.

Proper implementation

However when running the following test I found that the code above was not working as expected:

class Program
{
  static void Main(string[] args)
  {
    MyObject my = new MyObject();
    my.PropertyChanged += (sender, e) => Console.WriteLine(e.PropertyName);

    my.BoolProperty = true;
    my.IntProperty = 100;
    my.StringProperty = "something";
    my.ObjectProperty = 100;

    Console.WriteLine("Press Enter to exit...");
    Console.ReadLine();
  }
}

I was receiving only notifications for “StringProperty” and “ObjectProperty” properties, but nothing for “BoolProperty” and “IntProperty”.

After some investigation I found out that my problem was in “object” declaration in Expression<Func<T,object>>. Linq wraps value type instances with additional “Convert” expression so my check for “MemberExpression” was receiving null value during type casting.

This produced another slight modification that covered all aspects of expression passing:

public class ObservableObject<T> : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;

  protected virtual void OnPropertyChanged(Expression<Func<T, object>> property)
  {
    if (property == null || property.Body == null) return;

    var memberExp = property.Body as MemberExpression;
    if (memberExp == null)
    {
      UnaryExpression unary = property.Body as UnaryExpression;
      if (unary != null) memberExp = unary.Operand as MemberExpression;
      if (memberExp == null) return;
    }

    var handler = PropertyChanged;
    if (handler != null)
      handler(this, new PropertyChangedEventArgs(memberExp.Member.Name));
  }
}

As you can see I’ve added a fallback step to check whether an Unary Expression is actually being received. In this case I’m taking it’s Operand as the expected Member expression and this works like magic. Now my test output is correct and provides notification for all the properties.

Notes

The approach above consumes more CPU cycles than the common INotifyPropertyChanged use so it might slightly decrease performance. However I’m sure it may greatly reduce the cost of code support in future and save your time and money.

Feel free playing with the source code for this article.

Advertisements

2 thoughts on “Building refactoring-friendly observable objects in C#

  1. I find another way for resolve this problem :

    protected void OnPropertyChanged()
    {
    OnPropertyChanged(2);
    }

    private void OnPropertyChanged(int callingDepth)
    {
    var handler = PropertyChanged;
    if (handler == null)
    return;

    StackTrace stackTrace = new StackTrace(false);
    StackFrame propertyFrame = stackTrace.GetFrame(callingDepth);
    MethodBase method = propertyFrame.GetMethod();

    string name = method.Name;

    const string set_ = “set_”;
    if (name.StartsWith(set_, false, null))
    name = name.Substring(set_.Length);

    handler(this, new PropertyChangedEventArgs(name));
    }

    • Nice snippet but I’m not sure about performance and all usecase coverage to be honest. As I’ve mentioned the Setter is not the only place that may raise property value change notification and this approach won’t help in this case. Additionally you should take care of heap/stack fragmentation. I believe this snippet will provide a huge overhead.

      Regards,
      Denis

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s