I H8 PropertyChanged
...because I got used to auto-properties really quickly...
The most painful part of building entities to interact with the UI, (for me at least) is having to create properties that participate in the INotifyPropertyChanged
interface.
You know the ones I mean:
string _backingField;
public string MyProperty
{
get { return _backingField; }
set
{
if (!string.Equals(value, _backingField)
{
_backingField = value;
OnPropertyChanged("MyProperty");
}
}
}
You can improve the rate at which you can add these using a code-snippet, but there are annoying limitations, like having to rename the backing field to match the naming convention, the problem of the name being passed to OnPropertyChanged
becoming out-of-date with refactoring *(unless you are using C#6.0/VS2015 and "nameof()
") and that it's too verbose, you end up with a bunch of backing fields you don't want, and it's just fugly.
Wouldn't it be nicer if it looked like this?
public string MyProperty
{
get { return GetValue(() => MyProperty); }
set { SetValue(() => MyProperty, value); }
}
The above code can be generated easily enough with a snippet, it neatly aligns, requires no backing field, type-inference and generics work together to make it strongly-typed, and refactoring the property-name will not break anything.
What's the Trick?
Expressions! The GetValue
and SetValue
method's first parameter is an Expression<Func<T>>
- this allows you to pass a lambda expression into the method (with the property name) and supplies the generic-type context (T)
The rest of the trick is that the backing field is in fact a Dictionary<string, object>
that stores the values for all the properties.
protected Dictionary<string, object> m_values = new Dictionary<string, object>();
public T GetValue<T>(Expression<Func<T>> memberExpression)
{
var body = memberExpression.Body as MemberExpression;
if (body != null)
{
object value;
if (m_values.TryGetValue(body.Member.Name, out value))
{
return (T)value;
}
}
return default(T);
}
public bool SetValue<T>(Expression<Func<T>> memberExpression, T value)
{
if (EqualityComparer<T>.Default.Equals(value, GetValue(memberExpression)))
{
return false;
}
var body = memberExpression.Body as MemberExpression;
if (body != null)
{
m_values[body.Member.Name] = value;
OnPropertyChanged(body.Member.Name);
}
return true;
}