Friday, December 17, 2010

Jounce Part 8: Raising Property Changed

This will be a shorter bit for an interesting topic. I've noticed a few posts and discussion around raising the property changed notification. This continues to be the sore spot for MVVM. It creates quite a bit of ceremony for what is a fairly common piece of functionality that shouldn't be so hard to create.

Jounce supports all of the common ways of doing this, but there is a third that I learned from a colleague of mine (Keith Rome) that I added and think bears some attention.

The most common approach is to use the "magic string" technique (which many developers automatically cringe at) and do something like this:

public string FirstName
{
    get { return _firstName; }
    set
    {
        _firstName = value;
        RaisePropertyChanged("FirstName");
    }
}

The problem there of course is that it is too easy to type the property wrong. Coders who typically "cut and paste" might forget to update this and then they'll find themselves raising the wrong property.

The next iteration types the property a bit and gets rid of magic strings. It does this by allowing a lambda expression to be passed in. The expression tree is parsed and the property name is extracted. Some extra validation can even make sure it's inside a property - but there is still no guarantee you aren't raising the wrong property, you just know you are raising some property.

public string FirstName
{
    get { return _firstName; }
    set
    {
        _firstName = value;
        RaisePropertyChanged(()=>FirstName);
    }
}

So the third method is a little more safe. Not only does it avoid magic strings, but I can get out of the ceremony of passing a weird lambda expression, and I can guarantee it will raise the notification for the correct property. Before I talk about how this works, let me show you the code:

public string FirstName
{
    get { return _firstName; }
    set
    {
        _firstName = value;
        RaisePropertyChanged();
    }
}

This will throw an exception if it's not in a setter. And it will always capture the current property. I still have to make a call, but no magic strings and we've reduced the ceremony somewhat. How does it work?

The trick is the context of execution. All of these methods have a little overhead (the string method actually has the best performance) but in most applications if the performance hit is in your setters, you've probably got worse issues farther up the stack.

Speaking of the stack, that's exactly what we use. We simply walk the stack frames up from the setter to find the name of the setter method itself. The convention for properties is set_propName so we look up where we are at and grab the propName.

Here's the code:

public virtual void RaisePropertyChanged()
{
    var frames = new System.Diagnostics.StackTrace();
    for (var i = 0; i < frames.FrameCount; i++)
    {
        var frame = frames.GetFrame(i).GetMethod() as MethodInfo;
        if (frame != null)
            if (frame.IsSpecialName && frame.Name.StartsWith("set_"))
            {
                RaisePropertyChanged(frame.Name.Substring(4));
                return;
            }
    }
    throw new InvalidOperationException("NotifyPropertyChanged() can only by invoked within a property setter.");
}

Notice once we extract the property name, we just pass it along to the method that takes a happy string.

There you have it - another way to do the deed with a little more overhead but a lot less ceremony.

Addendum

Thanks to everyone for the excellent responses. Apparently this is a method that has been tried in the past. As Sergey Barskiy pointed out, the JITter will likely inline the method and break the stack frame. Scott Bussinger offered this link as well: The Story of a Wicked Little Bug. What's interesting is that you can mark the method (and I have) to avoid inlining, and it seems the consensus is that this works except on 64-bit systems. Currently, there is not a 64-bit Silverlight runtime. However, with future releases we'll have to revisit and determine whether or not this remains an issue. Ideally, I'd love to be able to do this:

[NotifyPropertyChanged]
public string MyProperty { get; set; }

And be done with it.

Jeremy Likness

9 comments:

  1. Weren't there some issues with using stack walking to solve the INPC problem? Check out http://dotneteers.net/blogs/vbandi/archive/2010/05/06/the-story-of-a-wicked-bug.aspx and http://www.timvw.be/little-inotifypropertychanged-helper/

    ReplyDelete
  2. Nice one! Have you tried to measure if any performance hit is implied with this method?

    ReplyDelete
  3. I know PostSharp can let you write the following code:

    [NotifyPropertyChanged]
    class MyClass { // ... }

    They ware exposing it in the form of aspects.
    More info: http://www.sharpcrafters.com/solutions/ui#data-binding

    ReplyDelete
  4. Like Russ said, cool way to raising property changed.. but i feel insecure to use this new method.
    The performance has big changes?

    ReplyDelete
  5. Russ: I measured the performance hit using code I've posted on pastebin: http://pastebin.com/MbHjxhRy

    It turns out it's pretty bad: about 2500x the cost of the "magic string" approach.

    I also wrote a blog post about this idea: http://philosopherdeveloper.wordpress.com/2010/12/23/my-oh-so-valuable-time-how-far-is-too-far/

    (I'm not trying to bash the idea; I will certainly agree that it's clever. But I'm with you, Jeremy, that the IDEAL solution would be an attribute that could take care of all this in a robust, compiler-supported manner.)

    ReplyDelete
  6. So I'll also add this - knowing going into it the trade-off between "clever" and "practical" versus performance: my favorite is the expression method. The stack frame seems to have many unknowns, and I just don't like magic strings.

    Here's some food for thought though: even at 2500x slower, if the net is milliseconds, then it still has a place. That's because of the trade-off between the developer risk and the application performance.

    If I am coding a form, then I don't really care if it is 1ms or 200ms, because the user is going to be slower than the validations. If the method reduces the developer's chance of creating an issue, I'm all for it.

    On the flipside, if I have a business algorithm that is spinning through hundreds of objects, then of course it matters and multiplies.

    But that's the catch: 99% of your INPC objects are INPC because they are bound to the UI, and hence a part of the user interaction. Therefore, running 1,000 iterations doesn't make sense because the user isn't that fast - if they user has an "instantaneous" response for all practical purposes (i.e. milliseconds, not hundreds of milliseconds) and the method helps with refactoring, maintenance, and reducing bugs, I'm all for it.

    ReplyDelete
  7. PS thanks everyone for weighing in and for the informative links that will help everyone reading this to explore it further.

    ReplyDelete
  8. Hi everyone,
    stack walk is slow and lambda expression is even slower. We have solution similar to well known lambda expression but almost as fast as string literal. See
    http://zamboch.blogspot.com/2011/03/raising-property-changed-fast-and-safe.html

    ReplyDelete
  9. Hello,

    The solution using the StackTrace won't work in Release mode as the Getter / Setter methods aren't shown within the Stack items.

    There it looks like we only have the lambda variant to avoid magic strings.

    ReplyDelete