PropertyOf and INotifyPropertyChanged.PropertyChanged without strings
When coding applications using design patterns such as MVP (Model-View-Presenter) or MVVM (Model-View-ViewModel), you'll find yourself using data binding and the INotifyPropertyChanged interface all over the place. This means referencing properties quite a lot.
For example, when you want to notify that the value of a property has changed, you can write NotifyPropertyChange("SomeProperty") where NotifyPropertyChange is a method that invokes the INotifyPropertyChanged.PropertyChanged event.
The major inconvenience of using a string to reference a property name, as in NotifyPropertyChange("SomeProperty"), is that it's a very brittle approach. If the name of the property changes or the property is removed during some refactoring operation on the source code, then it's easy to forget to update all the "string references".
Luckily, solutions exist to avoid this kind of issues.
infoof => PropertyOf, FieldOf, MethodOf, ConstructorOf
We don't have propertyof or infoof operators in C# (see 1, 2, 3 or 4), but we can use things such as PropertyOf thanks to expression trees.
You can learn more about such an approach on the C# FAQ blog and Patrick Smacchia's blog.Here is the source code, derived from the above articles, that I use in a utility class:
public static MethodInfo MethodOf<T>(Expression<Func<T>> expression)
{
var callExpr = expression.Body as MethodCallExpression;
// If the method gets a lambda expression that is not a method call,
// an exception is thrown
if (callExpr == null)
throw new ArgumentException("Expression \"" + expression +
"\" is not a valid method call.");
return callExpr.Method;
}
public static MethodInfo MethodOf(Expression<Action> expression)
{
var callExpr = expression.Body as MethodCallExpression;
// If the method gets a lambda expression that is not a method call,
// an exception is thrown
if (callExpr == null)
throw new ArgumentException("Expression \"" + expression +
"\" is not a valid method call.");
return callExpr.Method;
}
public static ConstructorInfo ConstructorOf<T>(
Expression<Func<T>> expression)
{
var newExpr = expression.Body as NewExpression;
// If the method gets a lambda expression that is not a constructor call,
// an exception is thrown
if (newExpr == null)
throw new ArgumentException("Expression \"" + expression +
"\" is not a valid constructor call.");
return newExpr.Constructor;
}
public static PropertyInfo PropertyOf<T>(Expression<Func<T>> expression)
{
var memberExpr = expression.Body as MemberExpression;
// If the method gets a lambda expression that is not a member access,
// for example, () => x + y, an exception is thrown
if (memberExpr == null)
throw new ArgumentException("Expression \"" + expression +
"\" is not a valid member expression.");
var property = memberExpr.Member as PropertyInfo;
if (property == null)
throw new ArgumentException("Expression \"" + expression +
"\" does not reference a property.");
return property;
}
public static FieldInfo FieldOf<T>(Expression<Func<T>> expression)
{
var memberExpr = expression.Body as MemberExpression;
// If the method gets a lambda expression that is not a member access,
// for example, () => x + y, an exception is thrown
if (memberExpr == null)
throw new ArgumentException("Expression \"" + expression +
"\" is not a valid member expression.");
var field = memberExpr.Member as FieldInfo;
if (field == null)
throw new ArgumentException("Expression \"" + expression +
"\" does not reference a field.");
return field;
}
INotifyPropertyChanged.PropertyChanged
PropertyOf can be used for invoking PropertyChanged without risking to pass an incorrect string.
Before we see how, let's review all the solutions I use to fire the INotifyPropertyChanged.PropertyChanged event. The following methods come from my base ViewModel (MVVM) class.
First, the most common implementation:
protected void NotifyPropertyChanged(String propertyName)
{
var handler = PropertyChanged;
if (handler == null)
return;
handler(this, new PropertyChangedEventArgs(propertyName));
}
Sample use: NotifyPropertyChanged("SomeProperty")
This exhibits what we want to avoid: a string reference.
The following version is relatively direct as well, but probably less useful:
protected void NotifyPropertyChanged(PropertyInfo property)
{
var handler = PropertyChanged;
if (handler == null)
return;
handler(this, new PropertyChangedEventArgs(property.Name));
}
Sample use: NotifyPropertyChanged(property), where property is a PropertyInfo instance you already have at hand.
The following one uses PropertyOf and ensures you'll get a compile-time error if you try to reference a missing or renamed property:
protected void NotifyPropertyChanged<T>(Expression<Func<T>> expression)
{
var handler = PropertyChanged;
if (handler == null)
return;
var propertyName = Helpers.PropertyOf(expression).Name;
handler(this, new PropertyChangedEventArgs(propertyName));
}
Sample uses: NotifyPropertyChanged(() => SomeProperty)
or NotifyPropertyChanged(() => SomeObject.SomeProperty)
protected void NotifyPropertyChanged()
{
var methodName = new StackFrame(1).GetMethod().Name;
if (!methodName.StartsWith("set_"))
throw new Exception("This overload of the NotifyPropertyChanged" +
"method must be called from a property setter.");
NotifyPropertyChanged(methodName.Substring("set_".Length));
}
Sample use:
public String SomePropertyThe above overload will extract the property name from the current call stack.
{
get { return _SomeProperty; }
set
{
if (value == _SomeProperty)
return;
_SomeProperty = value;
NotifyPropertyChanged();
}
}
Warnings:
- StackFrame.GetMethod may be considered expensive, depending on the context.
- This approach won't work if obfuscation has been applied to the binaries.
- It seems that this won't work either without debug symbols, although I haven't checked.
Another example: Windows Forms data binding
Of course, PropertyOf and its friends are not useful only for PropertyChanged. They can be used in several cases.
For example, here is a standard way of creating a data binding in Windows Forms:
txtCustomerName.DataBindings.Add("Text", Presenter, "CustomerName");
Let's decrypt the above for those of you not familiar with Windows Forms data binding. Here, txtCustomerName is a TextBox. "Text" is the name of the property of this TextBox on which you want to create a binding. Presenter is the "data source" object. "CustomerName" is the name of the property of the data source that you want to bind to the Text property.
The documentation is here.
Of course, what we want to avoid here is to use strings to reference properties. Here is what you can write with PropertyOf:
txtCustomerName.DataBindings.Add(No strings. In addition, it becomes clear when reading such code where the properties come from. No need to remember that the first parameter must designate a property of txtCustomerName and the third one a property of Presenter.
PropertyOf(() => txtCustomerName.Text).Name,
Presenter,
PropertyOf(() => Presenter.CustomerName).Name);
If you want to simplify your code, you can create a helper method such as the following, which uses PropertyOf internally:
AddBinding(txtCustomerName, () => txtCustomerName.Text,
Presenter, () => Presenter.CustomerName);
I'm sure you'll find more uses for these helpers.
Armed with these tools, you can now go back to writing code, but more reliable code.