Search This Blog

2009-06-01

C# 3.0 Tutorial -3:Lambda Expressions

Lambda Expressions

The term "lambda expression" sounds somewhat frightening at first, but there’s no reason to be sheepish. In fact, the lambda calculus – a very simple language where everything is expressed in terms of functions – dates back to the day before we had computers, making it some of the earliest theoretical Computer Science work.A lambda expression simply defines an anonymous function. A function is something that takes one or more parameters (just as a method does) and uses them in computing some value. That value becomes the return value for the function. In C# 3, the "=>" syntax is used to write a lambda expression. You place the parameters to the left of the arrow and the expression to compute to the right.For example, here is a function that adds one to the value it is provided with:

x => x + 1

How does this work? Well, it takes one parameter x and then returns the result of doing "x + 1". You can write a Lambda expression that multiplies to numbers quite easily too:

(x, y) => x * y

Here we have taken two parameters, x and y, and the result is the multiplication of them. Note that if we have more than one parameter, we have to place them in parentheses. How about if you do not wish to take any parameters? In this case, you put an empty set of parentheses in place of the parameter.

() => new Beer()

The above function takes nothing and returns beer; this is rarely implemented in the real world. If you want to do something more complex, you can supply a block to the right of the arrow. In this case, you should write a return statement, unless you do not wish to return a value (which is allowable, though not the common case).

(x, y) => {var result = x + y;return result;}

Using Lambda Expressions

At this point you could be forgiven for thinking, "well that's neat, but why?" C# 2.0 added support for anonymous methods. However, the syntax was rather verbose for a feature that, at least amongst some programmers, is used quite often. Let's look at a couple of examples where anonymous methods were used before and see the improvement that we get by using lambda expressions instead.In this first example, we will take a list of strings, sort them by length and then display the output. We use an anonymous method to give the comparisons.

Which is a lot neater. For a second example, suppose we are rendering some forum markup tags to HTML. We are going to match a tag with a regex, check if the tag is in a list of allowed tags and, if it is, render it to HTML. Otherwise, we'll just leave it unrendered. Here is the original implementation.

One difference you may have spotted between lambda expressions and the original anonymous method syntax is the absence of types on the parameters. You actually can write the types in if you wish:

(int x, int y) => x + y

Be aware that you need the parentheses for a single parameter if you're going to write a type annotation:

(int x) => x + 1

Even here, there is something more special going on, since nowhere have we declared the type of value that will be returned by the lambda expression. With anonymous methods we had to do that.

In the previous article, I talked about type inference. As a very quick recap, this involves working out the types of variables based on information available in the code rather than making the programmer write them in. This is exactly what is happening here. The interesting question, then, is where is the type information coming from this time?

When a method expects to be passed an anonymous method as a parameter, it uses a delegate type. This delegate type contains the types of the parameters. When a lambda expression is used, it is often being passed as a parameter. Therefore, the delegate type of the parameter will, in turn, enable to compiler to work out what the types of the lambda expression's parameters are.

If you're wide awake, you might be wondering what happens when you have a generic delegate type as a parameter of a method and pass a lambda expression there. And the answer is that yes, you can do this, generic types will be inferred and it should all work out just fine. In fact, some of what LINQ does depends on it working.

Conclusion

In this article we've seen extension methods and lambda expressions. I've taken the time to dive into some of the ugly details, but don't worry if some of them haven't sunk in just yet.

Extension methods offer some powerful new possibilities, but we need to take care in how we use them from a software engineering angle. Don't expect to be using them every day, but remember them for those times when they really are the right thing to use.

Lambda expressions, on the other hand, are for regular use. Even if you aren't doing much higher order programming today, if you plan on using LINQ you soon will be. The biggest hurdle most people have to get over is realizing that it is possible (conceptually, at least) to treat code the same as data. Once you get comfortable with that idea, using anonymous methods or lambda expressions doesn't feel so unusual. Practice and experience help. I'd recommend trying to learn a functional programming language, but if you're reading this you're probably wanting to get C# 3.0 cracked first.

In the next article in the series we'll look at object initializers and anonymous types. These make it easier to build up data structures and set initial values for fields in objects and structures. With that, we will have seen all of the language features that act as the building blocks for LINQ, which will be covered in the final part in the series.