INotifyPropertyChanged, The .NET 4.5 Way

posted on 01 Mar 2012 | .NET

This article is part of a series:

  1. INotifyPropertyChanged, The Anders Hejlsberg Way
  2. INotifyPropertyChanged, The .NET 4.5 Way
  3. INotifyPropertyChanged, The .NET 4.5 Way - Revisited
  4. INotifyPropertyChanged, The .NET 4.6 Way

Previously I discussed a novel new way of implementing INotifyPropertyChanged based on code I saw during a Build session.

As of yesterday, February 29th, the Visual Studio 11 & .NET 4.5 Betas are out, and included in the .NET 4.5 Beta comes a handy new feature, the CallerMemberName attribute. It is one of three new Caller Information attributes that have been added in this .NET Framework release.

It is joined by the CallerFilePath and CallerLineNumber attributes. These attributes tell the compiler to include information about the caller as a parameter when compiling a method call (there's no runtime logic involved).

With this new functionality we can code things like logging & tracing routines and INotifyPropertyChanged implementations without having to use string literalsslow reflection code, complex expression tree logic, or code weaving.

Leveraging CallerMemberName, we can now rewrite our previous implementation as the following:

public event PropertyChangedEventHandler PropertyChanged;

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

private int unitsInStock;
public int UnitsInStock
{
    get { return unitsInStock; }
    set 
    { 
        SetProperty(ref unitsInStock, value);
    }
}

Fast, clean, maintainable, and no outside dependencies!

Last, but not least, for those of you who may prefer a more conventional INotifyPropertyChanged approach, the updated .NET 4.5 MSDN documentation page for INotifyPropertyChanged uses the CallerMemberName attribute in it's implementation example. (-;