fredrik.eriksson

Coffee and a keyboard

C# lambda expressions and how they work

Lambda expressions is a language feature to declare functions/methods or expression trees. The function/methods is the part that most developers have been in contact with, probably while using LINQ. The expression tree is a little harder to grasp, but in short it’s a data structure that contains a tree representation of your lambda expression. If you have used LINQ2SQL, expressions trees is the think that makes it possible to navigate the LINQ expression and transform it to an SQL query.

Now to the questions I have got more and more often lately: How does Lambda expression work, and why doesn’t we need a new version of the CLR? Let’s start by looking at a small example:

public static void RunSnippet() {
  Func<int, int, int> add = (left, right) => left + right;
  WL("Example1: {0}", add(1, 2));

  int left2 = 1;
  int right2 = 2;
  Func<int> add2 = () => left2 + right2;
  WL("Example2: {0}", add2());
}

The above example demonstrates the two most common lambdas. First add that take parameters (left and right), and add2 having no parameters, but depends on two variable from another scope (left2, right2 from the RunSnippet static method scope). These two examples are interesting because they show two examples on how code is generated for two different lambdas. Lets start at looking at the code the compiler outputs for example 1:

private static int b__0(int left, int right) {
  return (left + right);
}

public static void RunSnippet()    {
  Func<int, int, int> add = new Func<int, int, int>(b__0);
  int tmpResult1 = add.Invoke(1, 2);
  WL("Example1: {0}", tmpResult1);
}

In this case the compiler creates a static method that represents the lambda expression, probably exactly what you would do if you hadn’t been using lambdas.

Example2:

private sealed class c__DisplayClass3 {
  public int left2;
  public int right2;

  public c__DisplayClass3() {
  }

  public int b__1() {
      return left2 + right2;
  }
}

public static void RunSnippet()    {
  c__DisplayClass3 tmp = new c__DisplayClass3();

  int left2 = 1;
  int right2 = 2;
  tmp.left2 = left2;
  tmp.right2 = right2;

  Func<int> add2 = new Func<int>(tmp.b__1);
  int tmpResult2 = add2.Invoke();
  WL("Example2: {0}", tmpResult2);
}

The code generated for example two is not as strait forward as in example one, this is because we have dependencies to variables in a method scope, and to get access to these the compiler have to generate a class that emulates this scope. Another reason is that to be sure that the local variable left2 and right2 is accessible when the method is invoked we either have to copy the value or hold a reference to an object, otherwise it may be removed by the garbage collector. This is more obvious if the lambda expression is returned from a method and executed later in another method as in the example bellow:

public static Action DoStuff() {
  string str = "important info";

  return () => WL("Str: {0}", str);
}

public static void RunSnippet() {
  Action a = DoStuff();
  // Do more stuff that takes time
  a();
}

I hope I have shown that there is no magic behind the lambda expression, and that the reason we don’t need a new CLR is that we do simple code generation already supported by the current CLR.