C#: Lambda Expressions

As a belated follow up to my post on C# Anonymous Methods, let’s look at a closely related feature of the CLS: lambda expressions.  Then let’s use a lambda expression to improve the code example from that earlier article.

Lambda Expressions were introduced in the 3.0 version of the Common Language Specification.  Lambdas are essentially a refinement of anonymous methods.  They allow you to define a block of code which operates on one or more input variables and returns a single result.  Let’s break down a simple lambda example, from the MSDN page on Lambda Expressions:

delegate int del(int i);
static void Main(string[] args)
    del myDelegate = x => x * x;
    int j = myDelegate(5); //j = 25

In this example, a delegate type is defined, del.  Then an instance of the delegate is created using a lambda expression.  Finally, the delegate is executed, and the return value is assigned to the integer variable j.  Let’s ignore everything but the lambda expression itself: x => x * x.

The first character of the expression actually defines a variable named x, which only exists inside the lambda expression.  In the MSDN example, when the lambda is actually executed, x will be assigned the input value of 5.

The => character sequence tells C# that your lambda expression operates on the variable x.  To oversimplify, everything to the left of => is an input variable declaration, and everything on the right is an expression which determines the return value.  In our examples, there will only ever be one input variable.

The x * x character sequence is the actual expression, the bit of code that gets executed.  So we could rewrite down the code above to this:

int x = 5;
int j = x * x;

…and achieve the same result.

Filtering Cars with Lambda Expressions

Returning to our anonymous methods example, we found that the FindAll method of the List<> generic class can take a delegate. The delegate is used to filter which items in the list are returned.

With Anonymous Methods

static void Main(string[] args)
    // Create a list of cars meeting criteria by calling the FindAll()
    // method of the List returned by CreateCars().
    List cars = CreateCars().FindAll(delegate(Car candidate)
        // Return whether the car matches our criteria.  FindAll()
        // expects our anonymous method to return a boolean, and
        // the compiler makes sure this is the case.
            candidate.Manufacturer == "Toyota" ||
            candidate.Manufacturer == "Nissan" &&
            candidate.Turbocharged == false;
    foreach(Car car in cars)

Instead, let’s replace that anonymous method block with a lambda expression.

With Lambda Expressions

static void Main(string[] args)
    List cars = CreateCars().FindAll(car =>
        car.Manufacturer == "Toyota" ||
        car.Manufacturer == "Nissan" &&
        car.Turbocharged == false );
    car.Turbocharged = true; // Invalid code, "car" does not exist.

In this case, our lambda expression creates the variable car, and then does the same set of conditional tests as the old example. Note that the car variable only exists in the scope of the lambda expression, just like in the anonymous method. For that reason, the second to last line of code would be a compiler error.

So What’s the Difference?

For the purposes of our example, not a whole lot:

  • The input variable and return types are inferred. C# is able to figure out what type car is by looking at where the lambda is used.   The type is actually defined in the CreateCars() method of our earlier example. C# looks at the return type of List and infers (or deduces) that a lambda expression operating on the FindAll method of that List would operate on a single input variable of type Car. Note that in the old anonymous methods example, C# was only able to infer the return type.
  • The lambda expression does not need curly braces, since it is a basic expression.
  • The lambda expression does not use the ugly delegate() syntax, thanks to the type inference.

Of course lambda expressions are a bit more complicated than I let on; they can declare multiple input variables, and they can also contain normal procedural code statements (such as calling a method — and that does require using curly braces).