When declarativeness goes away for performance
TLDR: The creation a C# expression tree is not cached, and using it to simulate a methodof keyword is terribly slow. This article is about reconsidering the use of this technique when performance is a concern.
During the development of my last projects, and it's been like that for a while, I've been used to look for ways to express programs in a more declarative or functional way.
LINQ is a pretty good tool to acheive that, as well as fluent interfaces, lamdbas and all those neat language features and tricks.
When the language is against you
But there are some times when the language is not there to support patterns, like with the use of the IPropertyChanged interface. The language (and the CLR for that matter) does not publicly support intercepting calls to properties or methods. That can actually be done through Transparent Proxies or dynamic proxy generation, but these are not supported on Windows Phone 7. The latter somehow will on WP7.1.
Anyway, using that interface requires raising an event with a string containing the name of the property that has changed.
The use of metadata in the form of strings is unfortunately not refactoring friendly, and if you change your property name, you've got a bug on your hands.
Since there is no methodof keyword in C#, you could say that the language is against you since there is no direct way to get the name of a property or method at compile time.
This can still be done through reflection with plumbing code that uses Expression Trees to work around it, and it works pretty fine.
That way you can easily write nice wrappers like this one :
public int MyProperty { get { return GetValue(() => MyProperty); } set { SetValue(() => MyProperty, value); } }
That way, you get both the type safety and the refactoring friendly use of INotifyPropertyChanged.
Where declarativeness does not shine
If you crack open the assembly for a property like this one with a disassembler, you get this :
public int MyProperty { get { return this.GetValue<int>( Expression.Lambda<Func<int>>( Expression.Property( Expression.Constant(this, typeof(MainPage) ), (MethodInfo) methodof(MainPage.get_MyProperty) ), new ParameterExpression[0])); } set { this.SetValue<int>( Expression.Lambda<Func<int>>( Expression.Property( Expression.Constant(this, typeof(MainPage)), (MethodInfo) methodof(MainPage.get_MyProperty) ), new ParameterExpression[0]), value); } }
That's a lot of code !
And worse, that's not the end of it, because you'll have to traverse the expression just to get the "methodof", and then call the "Name" property to get the string. All this for a string that will never change after you've compiled your assembly.
When declarativeness goes away for performance
But that would not be that bad if you executed that code once, or ran it on a desktop computer (or you don't care about performance). For desktop and server applications, where the cost of executing that kind of code is (almost) neglectible, you do not care much about that.
But if you execute that code a few million times, or run it on a Windows Phone 7 on the UI thread, you've got a problem. The expression is not cached, neither in a static variable or an instance variable, depending on the context. Sure, you could store it in an expression typed variable, and cache it manually that way, but you'd lose a bit declarativeness.
To make a small comparison, it takes 13 seconds on my Samsung Focus to execute 10,000 gets of the property using expressions, and takes 0.2 milliseconds to do the same using a simple string.
Pretty easy to choose, isn't it ?
That's where you lose the declarativeness away for performance, and cringe a little bit about it when you know that you'll have to chase a bug in the future because of a lazy rename. Still, you can have debug-time only code that walks up the stack and checks that the property actually exists, but you have to execute that code to find the bug.
Mitigating
Hopefully, there are tools like Postsharp that help in that regard, where a post processing step does the reflection once and for all, and injects that missing string. That's direction I'd rather not take, but since there's still no out of the box solution, that can be a good fit.
We're so used to techniques that avoid us to write boring code, but when performance is a concern, it is necessary to reconsider all coding reflexes and think twice before using them.