Partager via


What on Earth is a Lambda Expression?

Recently I've spoken with a few customers that have asked what a Lambda Expression is, which surprised me. It seems that a second wave of developers are now starting to use Lambdas (i.e. those that didn't adopt C# 3.0 immediately) and need some pointers, so this post is intended to help you understand what they represent.

I'm not going into the how, why, where, and more here – there are better posts that do. This is just "what the heck does that syntax mean?"

An Example Situation

I'm going to use an example common to software development in the modern world – filtering a list of Llamas according to their height and how prone to grumpiness they are (OK, perhaps only fairly common, and yes, I'm writing this on a Friday afternoon J).

Imagine we have a list of our favourite Llamas;

private static List<Llama> Llamas = new List<Llama>()

{

    new Llama { Name = "Larry", Height = 10, IsGrumpy = true },

    new Llama { Name = "Loulou", Height = 12, IsGrumpy = false },

    new Llama { Name = "Lara", Height = 8, IsGrumpy = true },

    new Llama { Name = "Lorry", Height = 4, IsGrumpy = true },

    new Llama { Name = "Laurel", Height = 20, IsGrumpy = false },

    new Llama { Name = "Louise", Height = 17, IsGrumpy = true }

};

Now imagine that I want to get a list of all Llamas that are both Tall and prone to Grumpiness. We might achieve that like this;

var results = new List<Llama>();

foreach (var llama in Llamas)

{

    bool include = llama.IsGrumpy && llama.Height > 9;

    if (include)

        results.Add(llama);

}

Refactoring to a Lamda

The problem is that's a lot of code to do some simple filtering... and we know there are these great Extension Methods in LINQ that allow us to do a Where command on a collection of objects.

Let's make some changes to the syntax to start heading in the right direction. Note that much of the content in the rest of this post is intentionally invalid C# syntax – I'm leading you to the solution. I'll call out the next time it becomes valid syntax again!

Imagine that Where took as input the name of a method to perform filtering of a list. Perhaps our code would look like this;

var results = Llamas.Where(Filter);

... and a helper method named Filter;

private bool Filter(Llama llama)

{

    return llama.IsGrumpy && llama.Height > 9;

}

That makes sense right? The Where method calls the Filter method with each Llama to check if it should be included in the results.

Anonymous Inline Methods

But our Filter method is only ever used by this single line of code to filter our Llamas, so we could just not bother declaring a separate method, and use an inline one instead, perhaps something like this (again, this isn't valid syntax like much of this post)...

var results = Llamas.Where(

bool Filter(Llama llama)

{

return llama.IsGrumpy && llama.Height > 9;

});

Here you can see we've in-lined the method, and gotten rid of the private keyword as it's no longer a class member. But why does it have a name then? Let's lose the name...

var results = Llamas.Where(

bool (Llama llama)

{

return llama.IsGrumpy && llama.Height > 9;

});

Our syntax is getting pretty concise, so stay with me... all this says is "this method returns a Boolean, and requires a Llama as input", and then defines the method body.

Implied Types

But the C# compiler is pretty smart right? So why am I telling it that this returns a Boolean, when it knows the Where method needs a Boolean, and it can check that the "IsGrumpy && Height > 9" statement is a Boolean expression? Let's lose that bool keyword...

var results = Llamas.Where(

(Llama llama)

{

return llama.IsGrumpy && llama.Height > 9;

});

And we also know that the Where method is operating on a List<Llama>, so the only possible input to this method is of type Llama... so let's stop needlessly calling this out in our code;

var results = Llamas.Where(

(llama)

{

return llama.IsGrumpy && llama.Height > 9;

});

Needless Constructs

The thing is, our method is such a simple single line Boolean expression, why do we need the return keyword? Or the semi-colon terminating the line? We know that's what it is doing. And we don't need the braces for a single line expression right?

var results = Llamas.Where(

(llama)

llama.IsGrumpy && llama.Height > 9

);

The problem is that if we remove some white space;

var results =

  Llamas.Where( (llama) llama.IsGrumpy && llama.Height > 9 );

... it gets pretty tough to read, so we need a new way of separating the input parameters from the body of our expression, and in C# that is the => operator;

var results =

  Llamas.Where( (llama) => llama.IsGrumpy && llama.Height > 9 );

Now we don't need the brackets around our input parameter as there's only one...

var results =

  Llamas.Where( llama => llama.IsGrumpy && llama.Height > 9 );

... and why use all those characters to spell out "llama" all the time when we could use "l"?

var results = Llamas.Where( l => l.IsGrumpy && l.Height > 9 );

Summary

It turns out that the last three statements above are valid Lambda Expressions that filter a list of Llamas for us. The intention of this code hasn't changed – it is still saying;

Here is a method that takes an input parameter named "l", and returns a Boolean result by executing the following expression against that parameter. Use this method to filter Llamas please!

So explain to yourself what this syntax would mean?

tallGrumpyLlamas.ForEach(l => Console.WriteLine(l.Name));

I really hope this has been a slightly different approach to explaining how to use Lambda Expressions... of course you should now do some reading up to understand related things like Expression<>, Func<>, Action<>, and more... enjoy!

I've attached the simplest example code in case you want to play with it yourself.

Program.cs

Comments

  • Anonymous
    April 16, 2010
    The comment has been removed

  • Anonymous
    April 16, 2010
    The comment has been removed

  • Anonymous
    April 16, 2010
    fantastic explanation Simon.  I've been using lambdas for years and find them incredibly powerful and concise constructs. Will forward this post onto the team I work with as not all of them are fans of lambdas and I think the above is one of the best explanation I've seen. Cheers, Terry

  • Anonymous
    April 16, 2010
    Thanks Terry! Really appreciate the feedback. Simon

  • Anonymous
    April 17, 2010
    I moved from C# to Ruby and Rails. This, along with MVC, Castle, etc. is a good change of direction. Will make the transition back, if ever, much easier.

  • Anonymous
    April 17, 2010
    so, if we know what the subject is, why mention it? Why not this? var results = Llamas.Where(IsGrumpy && Height > 9 );

  • Anonymous
    April 18, 2010
    I have been using lambda in production code for the last three or four months.  I am getting some feedback that this code is harder to understand that more traditional C#. What is your opinion on this?

  • Anonymous
    April 18, 2010
    Digital Media Minute pointer of the day. Nice job.

  • Anonymous
    April 18, 2010
    Not to mention that closures and collections go well very well (http://bit.ly/aR2nW). It is good to see that, along with Java getting closures, the awareness of this powerfull concept is rising.

  • Anonymous
    April 18, 2010
    The comment has been removed

  • Anonymous
    April 19, 2010
    Great explanation - very helpful.

  • Anonymous
    April 19, 2010
    Thanks Mike!

  • Anonymous
    April 21, 2010
    The comment has been removed

  • Anonymous
    April 21, 2010
    The comment has been removed

  • Anonymous
    April 28, 2010
    Neat explanation of Lambda expression.. I had hard time explaining lambda expressions to some of my team members, I could use your logic to teach them. thanks for sharing.

  • Anonymous
    April 28, 2010
    I've seen examples and explanations of lambdas along these lines quite a bit. What seems to be most confusing to me is how the "l" (each llama in the list of llamas) gets projected out of item in the list. There's an implicit for..each in there someplace - probably the implementation of the Where extension method. And many uses of lambdas don't involve LINQ-style expressions. So how do I cement this concept of something being "projected" (perhaps overloading a word improperly) into the parameter(s) to the lambda expression within the context of where the lambda is being used?

  • Anonymous
    April 28, 2010
    Simon, Such an elegant explanation. Congratulations. Mau

  • Anonymous
    April 28, 2010
    @ Donnie, This "projection" you mention is done by the Where method... the logic inside the method is something like; foreach (var l in llamas) {   if (lambdaExpression(l))      results.Add(l); } This means that it is the responsibility of the Where method to specify what signature the lambda expression must adhere to - obviously methods other than Where (such as ForEach or your own custom ones) might need different parameters. It turns out that Where does this by requiring the following type as the lambda expression parameter; Func<Llama, bool> This means "pass me a function that takes a Llama as input and returns a bool". The C# compiler validates that my expression fits this format. If writing my own Where-like function I could do this; public void CostProducts(   Product prod1,   Product prod2,   Func<Product, Product, Int32> comparer) {   // logic } The Func<Product, Product, Int32> states that I want a lambda expression that takes two products as input and returns an integer... so it might look like this in use; myobj.CostProducts(   p1,   p2,   (prod1, prod2) => prod1.Cost + prod2.Cost); There is also Action<>, that does the same as Func<> but needs no return value (i.e. it is a Sub not a Function in VB-speak!). I hope that helps - this is why I suggested reading up on Func<>, Action<>, and Expression<>. Simon

  • Anonymous
    April 28, 2010
    Another interesting aspect of C# related to lambdas are method groups. Say you have a function public void SendLlamaOutToPasture(Llama llamaToSend) {  //hurl Llama with catapult. } You would normally write it out something like the following tallGrumpyLlamas.ForEach(l => SendLlamaOutToPasture(l)); But C# provides an even more concise way to express this. tallGrumpyLlamas.ForEach(SendLlamaOutToPasture); As with Event handlers C# is able to see this is valid because the method matches the signature needed. It is behind the scenes converted to a function pointer and all is right with the world. :) This works anywhere lambdas would work. For your more complex lambdas I recommend using this by refactoring the lambda into a method. Then you can actually unit test the lambda. Another interesting thing is returning a lambda from a method. I use this when dynamically building Where statements for complex queries. (Say an Advanced Search page) public Expression<Func<Llama,bool>> IsLlamaOld(Llama llama) {  return l => l.IsGrumpy && l.Height > 9 }

  • Anonymous
    April 29, 2010
    I've read so many articles out there to understand lambda expression and I haven't understand it well until reading your article. Thanks a bunch! I hope you can write more stuffs like this

  • Anonymous
    May 01, 2010
    Great post. Have been using Lamda's for a couple of years but this stilled helped me clarify the way I think about them.   Thanks Terence

  • Anonymous
    May 25, 2011
    An excellent explanation of an often confusing subject for many. How about doing a series like this for other topics (e.g. delegates)?  

  • Anonymous
    February 20, 2012
    The way you explain it is really nice, and interesting. Thank you!

  • Anonymous
    March 02, 2014
    Great job of explaining the concept. Thanks.

  • Anonymous
    June 09, 2014
    This article is really amazing!Thank you!

  • Anonymous
    June 09, 2014
    This article is really amazing!Tank you!

  • Anonymous
    September 30, 2014
    The comment has been removed

  • Anonymous
    September 30, 2014
    Please you like it guys! I'm a firm believer that you don't truly understand something if you can't explain it in a simple way.

  • Anonymous
    May 14, 2015
    Hello !! This way of exlaining lambda expression is superb.....!! thanks a lot !