views:

4766

answers:

12

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 Event. I was thinking about using MethodBase.GetCurrentMethod.Name.Substring(4) but am a little uneasy about the reflection overhead.

+17  A: 

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).

Orion Adrian
+5  A: 

Yeah I see the use and simplicity of the function you are suggesting, but when considering the running cost due to reflection, yeah that is a bad idea, What I use for this scenario is having a Code snippet added properly to take advantage of the time and error in writing a property with all the Notifyproperty event firing.

Jobi Joy
+1  A: 

Additionally, we found an issue where getting a method name worked differently in Debug vs. Release builds:

http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/244d3f24-4cc4-4925-aebe-85f55b39ec92

(The code we were using wasn't exactly reflection in the way you suggested, but it convinced us that hardcoding the property name was the fastest and most reliable solution.)

rdeetz
A: 

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;
    }
}
Ian Oakes
+1  A: 

The problem with the reflection based method is that it's rather expensive, and isn't terribly quick. Sure, it is much more flexible, and less brittle towards refactoring.

However, it really can hurt performance, especially when things are called frequently. The stackframe method, also (I believe) has issues in CAS (e.g. restricted trust levels, such as XBAP). It's best to hard code it.

If your looking for fast, flexible property notification in WPF there is a solution -- use DependencyObject :) Thats what it was designed for. If you don't want to take the dependency, or worry about the thread affinity issues, move the property name into a constant, and boom! your good.

dhopton
+22  A: 

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);
Romain Verdier
This seems like overkill for a problem of property changing. While it maintains everything in refactorable code, you end up with a lot more code to maintain and that code is less readable than the original concept of OnPropertyChanged("Name") or a similar method.
Orion Adrian
+1 for explaining and confirming that "the subscriber of this event is likely to use reflection by calling, for instance, GetProperty".I was wondering what happened after the data object did OnPropertyChanged("Name").
Lernkurve
+3  A: 

Roman:

I'd say you wouldn't even need the "Person" parameter - accordingly, a completely generic snippet like the one below should do:

private int age;
public int Age
{
  get { return age; }
  set
  {
    age = value;
    OnPropertyChanged(() => Age);
  }
}


private void OnPropertyChanged<T>(Expression<Func<T>> exp)
{
  //the cast will always succeed
  MemberExpression memberExpression = (MemberExpression) exp.Body;
  string propertyName = memberExpression.Member.Name;

  if (PropertyChanged != null)
  {
    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  }
}

...however, I prefer sticking to string parameters with conditional validation in Debug builds. Josh Smith posted a nice sample on this: http://tinyurl.com/685gxg

Cheers :) Philipp

Philipp
I'm upvoting for the strings with conditional validation. It's simple and it works. At the end of the day, none of these reflection solutions solve the problem that I'd want solved - compile time checking of property change notifications.
17 of 26
A: 

You might want to avoid INotifyPropertyChanged altogether. It adds unnecessary bookkeeping code to your project. Consider using Update Controls .NET instead.

Michael L Perry
+3  A: 

You might be interessted in this discussion about

"Best Practices: How to implement INotifyPropertyChanged right?"

too.

jbe
+3  A: 

Another VERY NICE method I can think of is

Auto-implement INotifyPropertyChanged with Aspects
AOP: Aspect oriented programming

Nice article on codeproject: AOP Implementation of INotifyPropertyChanged

Peter Gfader
+7  A: 

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);
        }
    }
}
Phil
Note, there's actually a problem with the above code - it constrains T to the immediate subtype (since the immediate subtype specified T as itself). This means you couldn't use CreateArgs when dealing with properties in a class that is further down the hierarchy from Person.A simple fix to this is to make the CreateArgs method generic, i.e. CreateArgs<T> instead of having the Observable class generic.It is a bit more of a pain to use since you have to explicitly specify the type e.g.:static PropertyChangedEventArgs _firstNameChangeArgs = CreateArgs<Person>(x => x.FirstName);
Phil
Anything to forces me to change my base class is a big no-no. I don't exactly have the option to change the base class for something that already has to have a different base class or has a auto-generated base-class.
Orion Adrian
Orion... if that's the case, it should be easy enough to convert the above from a base class Observable to a helper class (e.g. 'ObservableHelper') that you can call explicitly. If you're interested, I could probably post an update which would cover the use case where you can't control which base class is used or don't want to introduce one. Though, where you do control the base class I do think there are advantages in using a base class; simplicity of usage being the main one.
Phil