LINQ Farm: Lambdas

Lambdas are a simple technology with an intimidating name. They sound like they are going to be difficult to understand, but in practice prove to be relatively trivial.

LINQ has an almost inordinate need for its users to declare a large number of small, simple delegates. The architects of C# decided that forcing the users of LINQ to declare delegates using standard C# 2.0 delegate syntax was an overly verbose option. They wanted to find a shorter, more concise way to accomplish the same task.

The syntax they settled on looks like this:

Func<int, int, int> myLambda = (a, b) => (a + b);

This is a shorthand way of writing code which is semantically equivalent to the following:

 public static int Add(int a, int b)
{
    return a + b;
}

Func<int, int, int> myDelegate = Add;

In the next few paragraphs I will compare these two ways of creating a delegate instance, and explain how they map back and forth.

It is obvious that the left hand side of the following two code fragments have the same meaning:

 Func<int, int, int> myLambda = (a, b) => (a + b);
Func<int, int, int> myDelegate = Add;

But how can the right hand side be the same?

It turns out that that the expression on the right hand side of the first statement is a shorthand way of writing a method semantically equivalent to the Add method. Just to be clear, a lambda is not a reference to the Add method, it is second method that does the same thing as the Add method.

Here is the lambda:

(a, b) => (a + b);

And here is the Add method:

 public static int Add(int a, int b)
{
    return a + b;
}

Here is the place in the Add method where we define the parameters it will take:

(int a, int b)

Here is the place in the lambda where we define the parameters that it will take:

(a, b)

Here is the place in the Add method where we define what it will return:

return a + b;

Here is the place in the lambda where we define what it will return:

(a + b)

As you can see, a lambda and a method do the same thing: they define a set of parameters and an executable body of code.

The type declarations for a lambda are resolved using a technique very similar to the one we employ for generic methods and generic delegates. To see how this works, look again at the full declaration for the lambda:

 Func<int, int, int> myLambda = (a, b) => (a + b);

The generic delegate Func says that the method being implemented takes two integers as parameters, and returns an integer. The compiler takes this information and applies it to the lambda. Behind the scenes it resolves (a, b) to (int a, int b) and defines the function such that the body of the lambda (a + b) returns an integer. Thus we give the compiler enough information to convert our lambda into a method that performs the same action as the Add method.

The => symbol is called a lambda operator and is usually pronounced “goes to.” Thus the lambda above can be read as “a comma b goes to a plus b,” or “a and b goes to a plus b.”

Though you will rarely need to do so, you can explicitly declare the type of parameters to a lambda expression:

 Func<int, int, int> f = (int a, int b) => (a + b);

For void functions that do not return a value, just use an empty set of parenthesis:

() => Console.WriteLine();

Lambdas have access to local variables:

 public static void UseLocal()
{
    int n;
    Func<int> func = () => { n = 6; return n; };
    n = func();
    Console.WriteLine(n); // Outputs the number 6
}

You might be familiar with anonymous methods from C# 2.0. Semantically, anonymous methods and lambdas are identical, but the lambda syntax is easier to use. As a result, there is probably no reason for you to use anonymous methods in the future. Below is a lambda and anonymous method side by side:

 Func<int, int, int> myLambda = (a, b) => (a + b);
Func<int, int, int> myAnonMethod = delegate(int a, int b)
{
    return a + b;
};

Both methods take two integers, add them together, and return the result. Commenting further on anonymous methods at this point would serve no purpose, since lambdas create the same result with less work.

In this post you have had a chance to look at lambdas. Their name is intimidating, and their syntax can be a bit confusing at first. Once you see beneath the facade however, this technology turns out to be relatively easy to master.

kick it on DotNetKicks.com