Getting rid of strings (2): use lambda expressions

June 12, 2008 at 12:08 PMAndre Loker

Intro

In the first article of this series I talked about the problems with strings in code. This article will show you how you can use lambda expressions and expression trees as another tool to avoid strings.

About Lambda Expressions

C# 3.0 brought a cool new feature call lambda expressions. On the one hand they are a nice abbreviation for anonymous delegates:

   1: Button b = /*..*/
   2: b.Click += (sender, e) => MessageBox(String.Format("{0} clicked", sender);

But there's an additional feature that might not be obvious to everyone. .NET 3.5 introduced the System.Linq.Expressions namespace which allows us to inspect a code expression tree. One special expression type is Expression<TDelegate> which derives from LambdaExpression. This expression type handles the expression tree represented by a lambda expression. Let's look at an example:

   1: public class Program {
   2:  
   3:   public static void ExpressionTest(Expression<Func<DateTime>> expression) {
   4:     Console.WriteLine("Expression body is '{0}'", expression.Body);
   5:     Console.WriteLine("Node type is {0}", expression.Body.NodeType);
   6:     Console.WriteLine("Expression body type is {0}", expression.Body.GetType());
   7:   }
   8:  
   9:   public static void Main(string[] args) {
  10:     ExpressionTest(() => DateTime.Now);
  11:   }
  12: }

ExpressionTest expects an expression representing a Func<DateTime>, that is a function that returns a DateTime. In the Main method ExpressionTest is invoked - not with an Expression<Func<DateTime>> but simply with a lambda expression with the Func<DateTime> signature. The C# 3.0 compiler will convert the lambda expression that is passed as argument to a expression tree with a top node of type Expression<Func<DateTime>>. The Body property of that expression contains the right hand side of the lambda expression.

Running this code prints:

   1: Expression body is 'DateTime.Now'
   2: Node type is MemberAccess
   3: Expression body type is System.Linq.Expressions.MemberExpression

The runtime type of the expression body is MemberExpression, which makes sense, because DateTime.Now represents access to a member (Now) of DateTime. Run the code in the debugger to see how the expression is represented in the expression tree.

I won't go into too much detail on expressions here. Browse through the documentation of the Expressions namespace to see what kind of expressions you can expect (and inspect for that matter).

How can lambda expressions help to avoid strings?

Expressions can be useful in situations where you need to provide the name of a member or a MethodInfo/PropertyInfo/FIeldInfo for that member. Ever needed to pass a MethodInfo somewhere? You'll most likely ended up with something like

   1: var info = typeof (DateTime).GetMethod("ToShortDateSting");
   2: Console.WriteLine(info.Name);

This compiles fine, of course, but when you run the code you'll get a null pointer exception at info.Name. Why? Because there's a typo in "ToShortDateSting". The method I was looking for is ToShortDateString (realize the 'r' in String). Did you see the typo at a glance? The situation gets worse if the name of the method changes, because now the code would break at runtime without being changed (see the first article to learn about problems with strings and refactoring).

Captain Lambda to the rescue

Here's an approach that is much more solid:

   1: var info = Reflect.GetMethod<DateTime>(dt => dt.ToShortDateString());
   2: Console.WriteLine(info.Name);

No strings attached so to speak. If you had a typo in ToShortDateString the code would not even compile. Additionally, the code is much more open to refactoring. But wait, how does it work? Here's the simple answer:

   1: public static class Reflect {
   2:   /// <summary>
   3:   /// Gets the MethodInfo for the method that is called in the provided <paramref name="expression"/>
   4:   /// </summary>
   5:   /// <typeparam name="TClass">The type of the class.</typeparam>
   6:   /// <param name="expression">The expression.</param>
   7:   /// <returns>Method info</returns>
   8:   /// <exception cref="ArgumentException">The provided expression is not a method call</exception>
   9:   public static MethodInfo GetMethod<TClass>(Expression<Action<TClass>> expression) {
  10:     var methodCall = expression.Body as MethodCallExpression;
  11:     if(methodCall == null) {
  12:       throw new ArgumentException("Expected method call");
  13:     }
  14:     return methodCall.Method;
  15:   }
  16: }

GetMethod expects an expression with a delegate of type Action<TClass>, that is a void method having a TClass as it's only argument. GetMethod checks that the expression passed in is a method call by casting the body to a MethodCallExpression. If the cast succeeds, the Method property already contains the MethodInfo that we were looking for. Thank you C# 3.0 compiler for doing the work for us :-)

A more practical example

In MonoRail the Controller class has a method called RedirectAction which - well - redirects the response to a new action. It expects the name of an action as its argument. So you might see code like this:

   1: public class HomeController : Controller {
   2:   
   3:   public void Index() {
   4:     if(!UserIsLoggedIn){
   5:         RedirectToAction("Login");
   6:     }
   7:   }
   8:  
   9:   public void Login() {
  10:   }
  11: }

This works fine but of course it suffers from all the string related problems I have been talking about so far. Let's see if we can use our new friend (Expression<TDelegate>) to improve the situation:

   1: public static class ControllerExtensions {
   2:   public static void RedirectToAction<TController>(this TController controller, Expression<Action<TController>> expression) where TController : Controller {
   3:     var methodCall = expression.Body as MethodCallExpression;
   4:     if (methodCall == null) {
   5:       throw new ArgumentException("Expected method call");
   6:     }
   7:     controller.RedirectToAction(methodCall.Method.Name);
   8:   }
   9: }

Now we have an extension method that we can use instead of the original RedirectToAction:

   1: public class HomeController : SmartDispatcherController {
   2:  
   3:   public void Index() {
   4:     // RedirectToAction("Login");
   5:     this.RedirectToAction(c => c.Login());
   6:   }
   7:  
   8:   public void Login(){
   9:   }
  10: }

Is this cool or what? Once again we got rid of a string. You can redirect to a method with parameters as well:

   1: public void Index() {
   2:   this.RedirectToAction(c => c.ShowItem(0));
   3: }
   4:  
   5: public void ShowItem(int id){
   6: }

You can pass any value to the "call" to ShowItem that you like. Remember: the expression is only examined but not executed. If you want to pass an actual value to the redirected action, create extension methods for the RedirectToAction overloads that accept parameters. I won't show this here because it is not too hard to implement (and I'm only showing some examples here anyway).

Properties

You can also get a PropertyInfo (and similarly a FieldInfo) using Lambdas, here's an example:

   1: public static class Reflect {
   2:   public static PropertyInfo GetProperty<TClass, TValue>(Expression<Func<TClass, TValue>> expression) {
   3:     var memberExpression = expression.Body as MemberExpression;
   4:     if (memberExpression == null || !(memberExpression.Member is PropertyInfo)) {
   5:       throw new ArgumentException("Expected property expression");
   6:     }
   7:     return (PropertyInfo) memberExpression.Member;
   8:   }    
   9: }
  10:  
  11: // use:
  12: var dayProperty = Reflect.GetProperty<DateTime, int>(dt => dt.Day);
  13: Console.WriteLine(dayProperty.Name);

NB: the code shown above only works for properties that are not write-only (otherwise you will not be able to "return" the property value in the expression). I don't consider this a big limitations. How many write-only properties have you written in the past two months?

In the example above we have to provide both the type of the class as well as the type of the property. It makes the code slightly less elegant. We can improve it like this:

   1: public static PropertyInfo GetProperty<TClass>(Expression<Func<TClass, object>> expression) {
   2:     MemberExpression memberExpression;
   3:     // if the return value had to be cast to object, the body will be an UnaryExpression
   4:     var unary = expression.Body as UnaryExpression;
   5:     if (unary != null) {
   6:       // the operand is the "real" property access
   7:       memberExpression = unary.Operand as MemberExpression;
   8:     } else {
   9:       // in case the property is of type object the body itself is the correct expression
  10:       memberExpression = expression.Body as MemberExpression;
  11:     }
  12:     // as before:
  13:     if (memberExpression == null || !(memberExpression.Member is PropertyInfo)) {
  14:       throw new ArgumentException("Expected property expression");
  15:     }
  16:     return (PropertyInfo) memberExpression.Member;
  17: }
  18:  
  19: var dayProperty = Reflect.GetProperty<DateTime>(dt => dt.Day);
  20: Console.WriteLine(dayProperty.Name);
  21:  

It's a bit more complicated, but still understandable.

Update 07/22/2008: RednaxelaFX came up with a third alternative. GetProperty stays the same as in the first (simpler) version, but we call the method differently:

   1: // use:
   2: var dayProperty = Reflect.GetProperty( (DateTime dt) => dt.Day);
   3: Console.WriteLine(dayProperty.Name);

By providing the type of the expression's argument explicitly the compiler is now able to infer the return value of the expression. Thanks RednaxelaFX for your comment!

Summing it up

Lambda expression trees are a great to tool that allows us to point to members without using strings. There are some limitations, though:

  • It only works for "compile time reflection". The expression tree is created during compilation, so you cannot get the name of "some member" of "some type" at runtime.
  • It won't work with static members
  • It will only work for members that are visible in the context where the expression is built. Non-public fields, properties and methods can therefore be tricky using this technique.

Still there are plenty of situations where you need to provide the MemberInfo of a public instance method/property (or it's name) known at compile time.

Posted in: C# | Patterns

Tags: , , ,

Comments (12) -

Matt Hawley
United States Matt Hawley says:

FYI - I had posted on the lambda based RedirectToAction Smile Here's a more thurough example that does what you really need it to do:

blog.eworldui.net/.../...ted-to-MVC-Preview-3.aspx

Andre Loker
Germany Andre Loker says:

Thanks for your reply. It's true that I did not handle passing parameters to actions in this article. It's not that hard, at least not for primitve parameters.

On the other hand the article is primarily about avoiding strings. The application on MonoRail is just an example (btw, maybe you missed that it's about MonoRail, not ASP.NET MVC)

Anyway, your article is really interesting!

I was wondering how they were using lamdas as expression trees in MVC.  

Thanks for the great article!

Frank Quednau
Germany Frank Quednau says:

Hi there,
looks good and I have seen a few of those cropping up lately...However, it would interest me what the performance penalty is. Obviously, providing a string literal will be blindingly fast, but how much slower is it to generate and analyze an expression?
Cheers...

Andre Loker
Germany Andre Loker says:

Interesting question. I've done some simple benchmarking comparing Type.GetMethod vs. lambda expressions. I found the expression tree to be roughly 13 times slower than GetMethod on my PC. Still the absolute duration of a call that returns the name of the provided method (such as in the example in the article) is about 5 microseconds on my equipment (Intel C2D E6750, 4GB RAM), so even if you throw in let's say a dozen of those calls in a single web request it won't matter very much. Most likely the simplest database call will outweigh all expressions by a hugh factor.

The code that builds the expression tree is generated by the C# compiler, so at least there's no runtime parsing involved. Also, the compiler uses TypeToken and MethodToken to look up relevant reflection information, so it's not too slow.

All in all, while it's certainly not the fastest thing in the world (I would not rely on expression tree inspection in a very tight loop of a 3D game engine) the cost is most likely affordable. The speed is traded in against code that is easier to refactor and less error prone.

Frank Quednau
Germany Frank Quednau says:

Thanks for the answer! It's not too bad. Another place where I have seen it on the web is in te context of the ugly PropertyChanged event of the INotifyPropertyChanged interface, quite popular in WPF-land, which wants a string literal denoting the property that changed. Here your method works just as well and indeed the performance penalty in the front-end is even less of an issue (if we aren't talking hundreds of data-bound properties raising their events at the same time)

Would it help to add caching to overcome the (minimal) performance overhead?

zoldmoha
Hungary zoldmoha says:

Brilliant!!!!

RednaxelaFX
People's Republic of China RednaxelaFX says:

About the code example for retriving PropertyInfo,
In the example above we have to provide both the type of the class as well as the type of the property. It makes the code slightly less elegant.
Actually there's no need to provide the type for the return type, the type inference engine will do that for us, if you put the type annotation in the right place, which is:

var prop = Reflect.GetProperty( ( DateTime dt ) => dt.Day );

Andre Loker
Germany Andre Loker says:

Thanks RednaxelaFX for your comment. Indeed I overlooked this possibility. I updated the article accordingly.

Nice, tnx for the info, i am enjoying read ur website..

m new in MVC and i m using the latest preview 3.its really fustrating that all of the help and example on the net (exept the northwind example which is very simple) is in preview 2 and there is no help or example on preview 3 . my problem is that i want to know how we can replace all the server controls (like gridview and menu ) in mvc .
and can u give me any example which is more then just links to other pages . also can u tell me what is diffrence between redirect and redirectToAction ,
Also where is the documentation of MVC
plz mail me back if any help