Ideas behind INotifyPropertyChanged implementation

INotifyPropertyChanged is a standard interface available form the early versions of .NET but still so popular. The general idea behind the implementation of this interface is awareness of the changes in the value of the properties in a class.

Now a days, it's heavily used in the implementation of the MVVM patterns and generally two way binding scenarios. The signature of this interface is as below. In case of any changes, we should manually fire the PropertyChanged event.

//
// Summary:
//     Notifies clients that a property value has changed.
public interface INotifyPropertyChanged
{
    //
    // Summary:
    //     Occurs when a property value changes.
    event PropertyChangedEventHandler PropertyChanged;
}

The classic implementation of this interface could be as simple as below:

class User : INotifyPropertyChanged
{
    private string _Name;
    public string Name
    {
        get
        {
            return _Name;
        }
        set
        {
            if (_Name != value)
            {
                _Name = value;
                OnPropertyChanged("Name");
            }
        }
    }
 
    public event PropertyChangedEventHandler PropertyChanged;
 
    private void OnPropertyChanged(string propertyName)
    {
        var pChanged = PropertyChanged;
        if (pChanged != null)
        {
            pChanged(thisnew PropertyChangedEventArgs(propertyName));
        }
    }
}

Well, there is nothing fancy about the this implementation. There is a single property called 'Name'. As the value of the property changes, two things happen. First, the new value will be set to the property. Second, OnPropertyChanged method will be invoked with passing the property name as a literal string.

Obviously the first problem here is passing a literal string to the method. That means if in future we decide to change the name of the property to i.e. FullName, we need to manually change the literal string from OnPropertyChanged("Name") to OnPropertyChanged("FullName") and Visual Studio reformatting tools can't help us in that regard. Of course, this brings overhead in maintenance of our real world classes.

As of C# 6, the new "nameof" operator is available which solves that problem. The new implementation is like below:

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

Now we can take advantage of the compile time type safety and rename our properties safely. Needless to say, the nameof operator is acting at compile time and there is no use of reflection at runtime. That means it does not have any negative performance effect. Nevertheless, the issue still exists for the earlier C# versions.

Using CallerMemberNameAttribute to improve usability:

There is an attribute in .NET Framework under System.Runtime.CompilerServices namespace, called CallerMemberName. We can make a good use of this attribute in conjunction with Optional Parameters feature to simplify our INotifyPropertyChanged implementation.

With using CallerMemberName attribute, the name of the caller of the method (or in our case, "Name") will be passed to the method and therefore there is no need of explicitly doing it.

class User : INotifyPropertyChanged
{
    private string _Name;
    public string Name
    {
        get
        {
            return _Name;
        }
        set
        {
            if (_Name != value)
            {
                _Name = value;
                OnPropertyChanged();
            }
        }
    }
 
    public event PropertyChangedEventHandler PropertyChanged;
 
    private void OnPropertyChanged([CallerMemberName]string propertyName = "")
    {
        var pChanged = PropertyChanged;
        if (pChanged != null)
        {
            pChanged(thisnew PropertyChangedEventArgs(propertyName));
        }
    }
}

In this piece of code two things have changed. First, we no longer send the name of the property to the OnPropertyChanged method. Second, we have added CallerMemberName attribute to the signature of the OnPropertyChanged and of course, made it an optional parameter. This optional parameter is set by C# compiler at compile time. That means CallerMemberName attribute is also acting at compile time and therefore there is no negative performance effect at Runtime.

More optimizations:

As you see in every property while setting a value, we actually do 3 things:

  1. Compare the old value with the new one
  2. Set the new value to the property
  3. Calling OnPropertyChanged

In the below implementation we make it just one line. The idea could be used as a base for a design or an architecture.

class User : INotifyPropertyChanged
{
    private string _Name = "";
    public string Name
    {
        get
        {
            return _Name;
        }
        set
        {
            OnPropertyChanged(ref _Name, value);
        }
    }
 
    public event PropertyChangedEventHandler PropertyChanged;
 
    private void OnPropertyChanged(ref string field, string value, [CallerMemberName]string propertyName = "")
    {
        if (!CheckIfEqual(field, value))
        {
            field = value;
            var pChanged = PropertyChanged;
            if (pChanged != null)
            {
                pChanged(thisnew PropertyChangedEventArgs(propertyName));
            }
        }
    }
 
    private bool CheckIfEqual<T>(T value1, T value2)
    {
        return EqualityComparer<T>.Default.Equals(value1, value2);
    }
}

Some more:

Not directly related but it is worth to mention there are two other attributes called CallerFilePathAttribute and  CallerLineNumberAttribute. The former is used to pass the file path of the caller file (i.e. C:\Users\...\Program.cs) and the latter is used to pass the line number of the invocation in the caller file (an integer). They could come up so helpful in some Logging scenarios.

Happy Programming!

No Comments

Add a Comment

As it will appear on the website

Not displayed

Your website