What is the best way to specify a property name when using INotifyPropertyChanged?
Most examples hardcode the property name as an argument on the PropertyChanged E
I did something like this once as an experiment, from memory it worked OK, and removed the need to hardcode all the property names in strings. Performance could be in issue if your building a high volume server application, on the desktop you'll probably never notice the difference.
protected void OnPropertyChanged()
{
OnPropertyChanged(PropertyName);
}
protected string PropertyName
{
get
{
MethodBase mb = new StackFrame(1).GetMethod();
string name = mb.Name;
if(mb.Name.IndexOf("get_") > -1)
name = mb.Name.Replace("get_", "");
if(mb.Name.IndexOf("set_") > -1)
name = mb.Name.Replace("set_", "");
return name;
}
}
The reflection overhead here is pretty much overkill especially since INotifyPropertyChanged gets called a lot. It's best just to hard code the value if you can.
If you aren't concerned about performance then I'd look at the various approached mentioned below and pick that that requires the least amount of coding. If you could do something to completely removes the need for the explicit call then that would be best (e.g. AOP).
The performance hit involved in the use of expression trees is due to the repeated resolution of the expression tree.
The following code still uses expression trees and thus has the consequent advantages of being refactoring friendly and obfuscation friendly, but is actually approx 40% faster (very rough tests) than the usual technique - which consists of newing up a PropertyChangedEventArgs object for every change notification.
It's quicker and avoids the performance hit of the expression tree because we cache a static PropertyChangedEventArgs object for each property.
There's one thing which I'm not yet doing - I intend to add some code which checks for debug builds that the property name for the supplied PropertChangedEventArgs object matches the property within which it is being used - at the moment with this code it is still possible for the developer to supply the wrong object.
Check it out:
public class Observable<T> : INotifyPropertyChanged
where T : Observable<T>
{
public event PropertyChangedEventHandler PropertyChanged;
protected static PropertyChangedEventArgs CreateArgs(
Expression<Func<T, object>> propertyExpression)
{
var lambda = propertyExpression as LambdaExpression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = lambda.Body as UnaryExpression;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = lambda.Body as MemberExpression;
}
var propertyInfo = memberExpression.Member as PropertyInfo;
return new PropertyChangedEventArgs(propertyInfo.Name);
}
protected void NotifyChange(PropertyChangedEventArgs args)
{
if (PropertyChanged != null)
{
PropertyChanged(this, args);
}
}
}
public class Person : Observable<Person>
{
// property change event arg objects
static PropertyChangedEventArgs _firstNameChangeArgs = CreateArgs(x => x.FirstName);
static PropertyChangedEventArgs _lastNameChangeArgs = CreateArgs(x => x.LastName);
string _firstName;
string _lastName;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
NotifyChange(_firstNameChangeArgs);
}
}
public string LastName
{
get { return _lastName; }
set
{
_lastName = value;
NotifyChange(_lastNameChangeArgs);
}
}
}
In .NET 4.5 (C# 5.0) there is a new attribute called - CallerMemberName it helps avoid hardcoded property names preventing the onset of bugs if developers decide to change a property name, here's an example:
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public void OnPropertyChanged([CallerMemberName]string propertyName="")
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private string name;
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged();
}
}
You might want to avoid INotifyPropertyChanged altogether. It adds unnecessary bookkeeping code to your project. Consider using Update Controls .NET instead.
Don't forget one thing : PropertyChanged
event is mainly consumed by components that will use reflection to get the value of the named property.
The most obvious example is databinding.
When you fire PropertyChanged
event, passing the name of the property as a parameter, you should know that the subscriber of this event is likely to use reflection by calling, for instance, GetProperty
(at least the first time if it uses a cache of PropertyInfo
), then GetValue
. This last call is a dynamic invocation (MethodInfo.Invoke) of the property getter method, which costs more than the GetProperty
which only queries meta data. (Note that data binding relies on the whole TypeDescriptor thing -- but the default implementation uses reflection.)
So, of course using hard code property names when firing PropertyChanged is more efficient than using reflection for dynamically getting the name of the property, but IMHO, it is important to balance your thoughts. In some cases, the performance overhead is not that critical, and you could benefit from some kind on strongly typed event firing mechanism.
Here is what I use sometimes in C# 3.0, when performances would not be a concern :
public class Person : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return this.name; }
set
{
this.name = value;
FirePropertyChanged(p => p.Name);
}
}
private void FirePropertyChanged<TValue>(Expression<Func<Person, TValue>> propertySelector)
{
if (PropertyChanged == null)
return;
var memberExpression = propertySelector.Body as MemberExpression;
if (memberExpression == null)
return;
PropertyChanged(this, new PropertyChangedEventArgs(memberExpression.Member.Name));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Notice the use of the expression tree to get the name of the property, and the use of the lambda expression as an Expression
:
FirePropertyChanged(p => p.Name);