My issue with all these examples is that none of them thought far enough ahead. Fine, they all show you how refer to properties without using hardcoded strings but they still require you to write lots of boilerplate code to raise the PropertyChanged event - boilerplate you have to write for every property. What I want is to be able to declare all my properties like:
public string Title {
get { return title; }
set { title = value; }
}
and yet still get my property change notifications. I also want this method to be reasonably high performance. I don't want every property change to have extra memory or CPU overhead as every developer expects that changing the value of a property will not do any complex calculations. So how can I accomplish this?
To start off with, we can all tell that it's impossible to achieve the required behaviour using just the snippet above. We're going to have to add (at least) one additional level of indirection. That means I should be able to implement my requirements using code like:
public string Title {
get { return title.Value; }
set { title.Value = value; }
}
The object 'title' must then contain all the logic required to raise the property changed notification. So what might this magical object look like?
public class ChangeNotifier<TValue>
{
Action<string> notifyHandler;
string propertyName;
TValue value;
public TValue Value {
get { return value; }
set {
if (!EqualityComparer<TValue>.Default.Equals(this.value, value)) {
this.value = value;
notifyHandler(propertyName);
}
}
}
public ChangeNotifier(Expression<Func<TValue>> expression, Action<string> notifyHandler)
{
if (expression.NodeType != ExpressionType.Lambda)
throw new ArgumentException("Value must be a lamda expression", "expression");
if (!(expression.Body is MemberExpression))
throw new ArgumentException("The body of the expression must be a memberref", "expression");
MemberExpression m = (MemberExpression)expression.Body;
this.propertyName = m.Member.Name;
this.notifyHandler = notifyHandler;
}
}
You're probably looking at this thinking "What the hell is this Expression<Func<TValue>> ? How do I even use that monstrosity?". Well... simples!
public class Book : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
ChangeNotifier<string> author;
ChangeNotifier<decimal> price;
ChangeNotifier<int> quantity;
ChangeNotifier<string> title;
public string Author {
get { return author.Value; }
set { author.Value = value; }
}
public decimal Price {
get { return price.Value; }
set { price.Value = value; }
}
public int Quantity {
get { return quantity.Value; }
set { quantity.Value = value; }
}
public string Title {
get { return title.Value; }
set { title.Value = value; }
}
public Book()
{
Action<string> notify = (propertyName) => {
var h = PropertyChanged;
if (h != null)
h(this, new PropertyChangedEventArgs(propertyName));
};
author = new ChangeNotifier<string> (() => Author, notify);
price = new ChangeNotifier<decimal> (() => Price, notify);
quantity = new ChangeNotifier<int> (() => Quantity, notify);
title = new ChangeNotifier<string> (() => Title, notify);
}
}
All that happens here is that when constructing the ChangeNotifier object, an Expression referencing the required Property is passed into the constructor, along with a delegate which will raise the PropertyChanged event. We parse that expression tree to retrieve the method name and store it. After that everything Just Works (tm) with little to no performance penalty. The days of writing boilerplate code for INotifyPropertyChanged are gone! You also have the benefit that you can't make a mistake writing the boilerplate code because you don't write it anymore!
12 comments:
Nice.
But I have a suggestion. How about another constructor that takes the PropertyChanged event. Then you can bury the "notify" action inside the ChangeNotifier.
As the notify action looks like boiler plate, it should make using it simpler.
This blog won't accept my code snippet (the lambda defs look too much like html) so I've switched the offending chars to ^. But I'm sure you'll understand after you change them and re-indent.
public ChangeNotifier(Expression^Func^TValue^^ expression, PropertyChangedEventHandler handler)
: this(
expression,
(propertyName) =^
{
var h = handler;
if (h != null)
h(this, new PropertyChangedEventArgs(propertyName));
}
) { }
2 suggestions
Add an user operator and you can remove ugly foo.Value
public static implicit operator TValue (ChangeNotifier < TValue > v)
{
return v.value;
}
Hide ChangeNotifier ctor via a factory method so type inference can work
If you are interested how this can be done in dynamic language (IronPython), see my blog post.
Sry, premature optimization.
First, it has at least a 4 times higher memory footprint per property (the pointer to the change notifier instance plus the three fields in that instance) than a regular one.
Second, what's with:
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName { get { return String.Concat(LastName, ", ", FirstName); }
Now when FirstName changes, obviously FullName has to send notification as well. No easy way to extend things here to enable that quite common scenario.
Best way I figured still is using T4 to create metadata classes and put boilerplate functionality in base classes or helper classes, so you can write:
private string _firstName;
public string FirstName { get { return _firstName; } set { base.Set(ref _firstName, value, Props.FirstName, Props.FullName); }
private string _lastName;
public string LastName { get { return _lastName; } set { base.Set(ref _lastName, value, Props.LastName, Props.FullName); }
public string FullName { get { return String.Concat(LastName, ", ", FirstName); }
If someone complains that this requires using non-POCO, then I reply, well we shouldn't be using POCOs anyway, we should be using POIs (plain old interfaces). ;-)
Another way to implement it is using AOP, e.g. http://thetreeknowseverything.net/2009/01/21/auto-implement-inotifypropertychanged-with-aspects/
Then you don't need to type code at all except decorating you class (or properties) by an attribute.
To get rid of the remaining boilerplate code, simply add another level of indirection - don't care about INotify in your entity at all, but let the properties implement it themselves!
That's what Ayende's Observable does: http://ayende.com/Blog/archive/2009/08/08/an-easier-way-to-manage-inotifypropertychanged.aspx
No expression trees, no AOP/T4, may easily be extended with support for interdependent properties.
public Observable< string> FirstName { get; private set; }
public Observable< string> LastName { get; private set; }
public Observable< string> FullName { get; private set; }
.ctor
{
FirstName = new Observable< string>();
LastName = new Observable< string>();
FullName = Observable.From(() => FirstName.Value + LastName.Value, FirstName, LastName);
}
Aren't Microsoft's dependency properties relevant here? http://msdn.microsoft.com/en-us/library/ms752914.aspx
Not really as it is a common mistake to make a view model inherit from DependencyObject (to get the plumbing for DPs) when all that is needed is the INotifyPropertyChanged.
After all a view model should be just a POCO with that exception as in my and many other's opinion it is not desirable to dump it into the visual tree and any dependency property mandates typically correlated back to 'animate this' or 'get value from data binding'.
Of course if you need something to participate visually it is often simply a binding off the view model however one must ask why a data template driven solution is not possible.
In other words, the view is 'projected on', the view model exposes the contextual data needed to project and the data templates represent the visual elements created via the view model data binding they have *see DataTemplateSelector and ItemContainerGenerator for more.
Yet another way... DPs in the view model is often a sign there is a design problem the above are vastly more reusable, flexible and extensible via decoupling. A DP couples your view technology in many cases with an otherwise reusable item.
Damon Carr
@Andy Pook: I'll do another blogpost on your idea and go into some details.
@Marek: The factory method is definitely a good idea, though I'm not keen on the implicit conversion.
@anonymous: Sure, you have '4 times the memory usage per property', but it's a static bloat in the region of a dozen bytes. There are zero extra runtime allocations, which is the more important part.
@kha: An apples and oranges comparison ;) My aim was to give standard INotifyPropertyChanged with no boilerplate while still offering standard property getters/setters. It is still trivial to do dependent properties with my implementation, I'll do that in the next blogpost.
@blake: INotifyPropertyChanged is definitely a poor mans version of a DependencyProperty in my eyes. Though there are places where it's a lot more useful than having to subclass from DependencyObject.
I don't like the
public Observable< string> FirstName { get; private set; }
because when I model my little world, the entities should be agnostic about if a property is observable or not. Ideally, it should be configurable.
To that DependencyProperty proposal I want to add that those are also not the most performant things on earth. ;-)
Actually one very good argument against the Observable pattern is that it makes read-only properties impossible to enforce at compile time. You convert what would be a compile time error to a runtime error, never a good thing.
For example assume that FirstName is supposed to be read only:
public Observable< string> FirstName { get; private set; }
FirstName.Value = "Bob";
You've just allowed the user to compile code which normally they couldn't. There are no benefits to the Observable method that I can think of, only downsides.
Another downside is that in the PropertyChanged event, you lose the 'sender' argument - so you no longer know which object actually changed.
I usually do blogging and for that purpose the best free wordpress themes are perfect. It just have smooth programming ability.
Post a Comment