Microsoft should have implemented something snappy for INotifyPropertyChanged
, like in the automatic properties, just specify {get; set; notify;}
I
Here is a Unity3D or non-CallerMemberName version of NotifyPropertyChanged
public abstract class Bindable : MonoBehaviour, INotifyPropertyChanged
{
private readonly Dictionary _properties = new Dictionary();
private static readonly StackTrace stackTrace = new StackTrace();
public event PropertyChangedEventHandler PropertyChanged;
///
/// Resolves a Property's name from a Lambda Expression passed in.
///
///
///
///
internal string GetPropertyName(Expression> property)
{
var expression = (MemberExpression) property.Body;
var propertyName = expression.Member.Name;
Debug.AssertFormat(propertyName != null, "Bindable Property shouldn't be null!");
return propertyName;
}
#region Notification Handlers
///
/// Notify's all other objects listening that a value has changed for nominated propertyName
///
///
internal void NotifyOfPropertyChange(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
///
/// Notifies subscribers of the property change.
///
/// The type of the property.
/// The property expression.
internal void NotifyOfPropertyChange(Expression> property)
{
var propertyName = GetPropertyName(property);
NotifyOfPropertyChange(propertyName);
}
///
/// Raises the event directly.
///
/// The instance containing the event data.
internal void OnPropertyChanged(PropertyChangedEventArgs e)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
#endregion
#region Getters
///
/// Gets the value of a property
///
///
///
///
internal T Get(Expression> property)
{
var propertyName = GetPropertyName(property);
return Get(GetPropertyName(property));
}
///
/// Gets the value of a property automatically based on its caller.
///
///
///
internal T Get()
{
var name = stackTrace.GetFrame(1).GetMethod().Name.Substring(4); // strips the set_ from name;
return Get(name);
}
///
/// Gets the name of a property based on a string.
///
///
///
///
internal T Get(string name)
{
object value = null;
if (_properties.TryGetValue(name, out value))
return value == null ? default(T) : (T) value;
return default(T);
}
#endregion
#region Setters
///
/// Sets the value of a property whilst automatically looking up its caller name.
///
///
///
internal void Set(T value)
{
var propertyName = stackTrace.GetFrame(1).GetMethod().Name.Substring(4); // strips the set_ from name;
Set(value, propertyName);
}
///
/// Sets the value of a property
///
///
///
///
internal void Set(T value, string propertyName)
{
Debug.Assert(propertyName != null, "name != null");
if (Equals(value, Get(propertyName)))
return;
_properties[propertyName] = value;
NotifyOfPropertyChange(propertyName);
}
///
/// Sets the value of a property based off an Expression (()=>FieldName)
///
///
///
///
internal void Set(T value, Expression> property)
{
var propertyName = GetPropertyName(property);
Debug.Assert(propertyName != null, "name != null");
if (Equals(value, Get(propertyName)))
return;
_properties[propertyName] = value;
NotifyOfPropertyChange(propertyName);
}
#endregion
}
This code enables you to write property backing fields like this:
public string Text
{
get { return Get(); }
set { Set(value); }
}
Furthermore, in resharper if you create a pattern/search snippet you can then also automate you're workflow by converting simple prop fields into the above backing.
Search Pattern:
public $type$ $fname$ { get; set; }
Replace Pattern:
public $type$ $fname$
{
get { return Get<$type$>(); }
set { Set(value); }
}