Getting rid of strings (2): use lambda expressions

June 12, 2008 at 12:08 PMAndre Loker


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 {
   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:   }
   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 {
   3:   public void Index() {
   4:     if(!UserIsLoggedIn){
   5:         RedirectToAction("Login");
   6:     }
   7:   }
   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 {
   3:   public void Index() {
   4:     // RedirectToAction("Login");
   5:     this.RedirectToAction(c => c.Login());
   6:   }
   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: }
   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).


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: }
  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: }
  19: var dayProperty = Reflect.GetProperty<DateTime>(dt => dt.Day);
  20: Console.WriteLine(dayProperty.Name);

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: , , ,