INotifyPropertyChanged, The .NET 4.5 Way - Revisited
This article is part of a series:
- INotifyPropertyChanged, The Anders Hejlsberg Way
- INotifyPropertyChanged, The .NET 4.5 Way
- INotifyPropertyChanged, The .NET 4.5 Way - Revisited
- INotifyPropertyChanged, The .NET 4.6 Way
In what is what is becoming a never ending topic on my blog, I have stumbled across yet another interesting implementation of INotifyPropertyChanged.
This implementation comes to us via the C# / XAML Windows 8 Metro application project templates in the Visual Studio 11 beta. In the project templates we are given a base class which implements INotifyPropertyChanged, BindableBase.
Here is the class in it's entirety:
using System.ComponentModel;
using System.Runtime.CompilerServices;
/// <summary>
/// Implementation of <see cref="INotifyPropertyChanged" /> to simplify models.
/// </summary>
[Windows.Foundation.Metadata.WebHostHidden]
public abstract class BindableBase : INotifyPropertyChanged
{
/// <summary>
/// Multicast event for property change notifications.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Checks if a property already matches a desired value. Sets the property and
/// notifies listeners only when necessary.
/// </summary>
/// <typeparam name="T">Type of the property.</typeparam>
/// <param name="storage">Reference to a property with both getter and setter.</param>
/// <param name="value">Desired value for the property.</param>
/// <param name="propertyName">
/// Name of the property used to notify listeners. This
/// value is optional and can be provided automatically when invoked from compilers that
/// support CallerMemberName.
/// </param>
/// <returns>
/// True if the value was changed, false if the existing value matched the
/// desired value.
/// </returns>
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(storage, value))
{
return false;
}
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
/// <summary>
/// Notifies listeners that a property value has changed.
/// </summary>
/// <param name="propertyName">
/// Name of the property used to notify listeners. This
/// value is optional and can be provided automatically when invoked from compilers
/// that support <see cref="CallerMemberNameAttribute" />.
/// </param>
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
This implementation, like my INotifyPropertyChanged, The .NET 4.5 Way post, uses the new CallerMemberName attribute in .NET 4.5. Where it differs though, is rather then using the EqualityComparer<T>.Default property to create a generic comparison, the code simply uses Object.Equals().
I knew Object.Equals() checks to see if two objects have the same reference in order to determine equality. That seemed perfectly acceptable to me. But what about value types?
Turns out, Object.Equals() handles those in a generic, but clever way:
The default implementation of Equals supports reference equality for reference types, and bitwise equality for value types. Reference equality means the object references that are compared refer to the same object. Bitwise equality means the objects that are compared have the same binary representation.
This is a lot less complicated (and most likely faster) then using EqualityComparer<T>.Default. There is, however, a subtle difference here in behaviour. Using EqualityComparer<T>.Default will do the following:
The Default property checks whether type T implements the System.IEquatable(Of T) interface and, if so, returns an EqualityComparer(Of T) that uses that implementation. Otherwise, it returns an EqualityComparer(Of T) that uses the overrides of Object.Equals and Object.GetHashCode provided by T.
So if you have custom equality comparisons implemented for some of your types, the EqualityComparer<T>.Default approach will most likely serve you better.
The one other distinct difference this INotifyPropertyChanged implementation has over all of my previous examples, is that SetProperty<T>() returns true or false based on whether or not the value was updated. This can be very useful if you have more sophisticated property logic that needs to do more then just raise the event if value changes.
All in all, I think I like this implementation the best.