Partager via


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

Comments

  • Anonymous
    June 27, 2008
    You've been kicked (a good thing) - Trackback from DotNetKicks.com

  • Anonymous
    June 27, 2008
    A nuisance, of course, is that var doesn't like bare lambdas -- it cannot tell whether the lambda should be an expression or a function without additional information. So helper methods can be useful: given Func<T, R> NewFunc<T, R>(Func<T, R> func) { return func; } Expression<Func<T, R>> NewExpr<T, R>(Expression<Func<T, R>> expr) { return expr; } you can then write var fun = NewFunc((int i) => i + 1); var expr = NewExpr((int i) => i + 1);

  • Anonymous
    June 28, 2008
    Thanks Keith. Good tips.

  • Anonymous
    June 28, 2008
    I may have missed it, but is there a way to declare a lamba function that takes arguments but does not return a value?

  • Anonymous
    June 28, 2008
    Returning a value from a lambda is implicit. If you define the lambda: (str) => Console.WriteLine(str) It will take the str and return the return value of Console.WriteLine - which is void. Also, you can use lambdas for more than one operation, like anonymous delegates, by using { } (i) => {    Console.WriteLine("Calculating f(" + i + ")");    return f(i); }

  • Anonymous
    July 02, 2008
    Jack: you would use an Action parameter type rather than a Func parameter type.

  • Anonymous
    July 02, 2008
    Hi Charlie, Nice article! I still use anonymous methods or delegates when there will be more then a few lines of code. How about you?

  • Anonymous
    July 03, 2008
    Hi there, nice article.... I blogged on the Expression part of the lambdas... have a look and see if you like it http://marlongrech.wordpress.com/2008/01/08/working-with-expression-trees-part-1/

  • Anonymous
    July 03, 2008
    My latest in a series of the weekly, or more often, summary of interesting links I come across related to Visual Studio. Greg Duncan posted a link to the DevExpress announcement of AgDataGrid Suite Beta 1 , available free of charge to Silverlight developers

  • Anonymous
    July 03, 2008
    Hi Charlie, This is a good explanation of lambdas. I'm going to update my post on lambdas to link to you. http://initializecomponent.blogspot.com/2008/07/fun-with-c-30-3-lambda-expressions.html

  • Anonymous
    July 09, 2008
    Anonymous delegates don't pass our code quality requirements in code reviews and are not allowed in our production system unless there is a really really important reason to use one. Lambdas will follow the same stringent no-use requirement. We made this simplification because we had several developers use them in many many parts of our core applications not because they were needed but because they wanted to be overly creative with the code. This just made the existing systems significantly harder and therefore more costly to maintain given we shoot for code to be easily maintainable by someone with 1 to 2 years of experience.   It may be conterintuitive to use simpler language features but we've found that limiting code to basic language features and simple algorithms results in better code weighed over a 5+ year life-cycle.   We expend extra effort to put quality business logic/decision information in our code comments over fancier coding methods.  This has dramatically lowered the cost of our production systems measured out over their lifespan given that most of the cost of software systems comes after they first go into production (75% or more).  Therefore, the upfront costs to develop a new system can be a little higher if we get significantly least costly and easier to maintain systems once they are put into production.

  • Anonymous
    July 10, 2008
    Ted, I understand and respect your desire to keep your codebase simple and clean. Lambdas, however, are one of the technologies that enable LINQ, and LINQ queries can be a means of significantly simplifying a codebase. For instance, it is often possible to take a series of lengthy, nested for or while loops, and convert them into a single short, easy to maintain LINQ query expression that uses lambdas. By the same measure, LINQ to XML and lambdas can often be a much simpler alternative to long and hard to understand DOM, XQuery or XPATH statements, and LINQ to SQL can help eliminate hard to read and maintain code that contains embedded SQL strings that appear as string literals. The search for simplicity and clarity is necessary if one wants to write code that can be easily maintained, so I back your effort to achieve that goal. Still, new technologies such as LINQ are introduced into C# specifically to aid people who are trying to achieve the goals you outline. LINQ is an example of a new technology that enables developers to write code that is at once easier to read, easier to write, and easier to maintain.

  • Charlie
  • Anonymous
    July 16, 2008
    Ted, I am in a similar position to your developers. Anonymous delegates are pretty much banned on our projects because our architecture team doesn't understand them (although they are honest enough to admit this). This makes our hopes of moving on to LINQ slim at best. This would not be much of an issue, except that LINQ, lambdas and associated technology are getting traction in the wider world. I fear that if things don' change soon, I'll have no choice but to find a way to get experience in those technologies, since getting stuck in an island of recalcitrance would be a bad career move.

  • Anonymous
    July 17, 2008
    The comment has been removed

  • Anonymous
    July 17, 2008
    echostorm: Aren't you being harsh? Are DB devs all "jr. developers" because the SQL language is intentionally minimized? It's not just the commands you do/don't use, but how you use them. Perhaps Ted's devs would write a intro doc for lambda functions, with a proper usage guideline. Point to the lambda doc in the file headers of source files where lambdas are used + comments mentioning "// This lambda function creates ..."

  • Anonymous
    July 17, 2008
    Disclaimer: My opinions, not my employers...

  • Anonymous
    July 17, 2008
    Ted,   If your engineers are confused by anonymous delegates then perhaps they should sit down with a book and spend the 10-30 minutes it takes to master the concept.  Seriously, it isn't exactly rocket science. Our company is taking the opposite approach and we are moving many of of our old .NET 1.1 and 2.0 projects to .NET 3.5. LINQ is the catalyst and personally, I get the shakes thinking about having to work on another .NET project without it. --Joe

  • Anonymous
    July 17, 2008
    The comment has been removed

  • Anonymous
    July 21, 2008
    Just the kind of explanation I was looking for. I have observed some discomfort among developers when Lambdas are mentioned. Your post puts it all in an easy to understand manner. Forwarding the link to all my team members.

  • Anonymous
    July 22, 2008
    Really nice explanation of Lambdas and anonymous delegates, Charlie.  I hope you teach this stuff in some fashion, the industry needs you!

  • Anonymous
    July 23, 2008
    The comment has been removed

  • Anonymous
    July 24, 2008
    Thanks everyone for this interesting conversation. I feel like I should add that I frequently hear conversations here at Microsoft about the impediments people have when they consider adopting a new tool. When a strong technology like LINQ comes along, we all want to make it easy for people to start using it as quickly as possible, but that is not always a simple proposition. When something good comes along that requires a change in thinking, it seems like there is always a group of early adopters, followed by a mainstream, and then a group that gets left behind. I don't think it is easy to pick one's way through the various technical options that are available, but when we do make the right choices, it leads to very pleasing results.

  • Anonymous
    July 24, 2008
    The LINQ aggregate operators allow you to perform simple math operations over the elements off a sequence.

  • Anonymous
    July 24, 2008
    The LINQ aggregate operators allow you to perform simple math operations over the elements in a sequence.

  • Anonymous
    July 24, 2008
    Hi. Great explanation! However, I have a small question. You wrote: " 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 }" Now, it might be just me, but you seem to be using n as the variable name both for the function and the lambda. { n=6; return n;} I guess that n is local to the lambda, and totally different scope than "int n" a bit above? Or is there a chance that the lambda has access to the local vars of the block where it is declared?(you wouldn't need to reassign it as in n = func() if that was the case?) To add to the other posts, I know this is just an example, but using the var n was enough to confuse me a bit and make me need some time to understand, making "maintenace" just that bit more expensive. And I've been programming for a few years... If LINQ is going to simplify things, and lambdas are the simpler way to do LINQ then by all means use them. But sometimes the fact that you can write something in fewer lines does not mean it is going to be easier to maintain. Who was it who said "I would have written you a shorter letter, but I didn't have the time"? Cheers, Enrique

  • Anonymous
    July 24, 2008
    I just wanted to stick my oar in regarding the simple code vs new technology debate. I personally favour keeping code simple and sticking to well-known coding constructs to ensure code remains easily readable and maintainable by a wide variety of developers. I think this is what Ted's post is getting at. It's interesting to note that microsoft have given us lambdas yet the code analysis tool they ship with VS 2005 tells us not to use reference and output parameters as they may introduce too much complexity. It's also interesting to note that they gave us anonymous methods and yet not long after we're advised not to use them as we've now got lambdas! In fact, I find anonymous methods to be a happy medium here - they are clearer in syntax than lambdas, and don't exactly take a lot more work to produce. I think if you showed examples of lambdas and anonymous methods to programmers who weren't familiar with either, they'd be much more likely to figure out the anonymous methods, and this is the important thing for me. But there is a bit of a chicken and egg thing here - if you never use lambda expressions because you think that other programmers may not be familiar with them, then how can you expect anyone to ever become familiar with them? So maybe just start using them, and comment well :-)

  • Anonymous
    July 25, 2008
    Yeah, just what we need. LESS readable code. I would rather have a few more lines and be readable. How is Func<int,long,int,string> = f(age,... better than (int age, long size... The former puts the type right next to the var name, making it much more readable.

  • Anonymous
    July 25, 2008
    Enrique, Actually no variables were defined in the scope of that lambda expression.  There's nothing inside the parens, which is where the local variables are defined for lambdas when they're set up as delegates. Perhaps this would be a better example to illustrate being able to access variables outside of the scope of the lambda expression: public void UseLocal() { int n; int[] nums = {0,1,2,3}; Func<int> func = () => { return nums.Last(); }; n = func(); Console.WriteLine("{0}", n); } However the same could be accomplished by: public void UseLocal() { int[] nums = {0,1,2,3}; int n = nums.Last(); Console.WriteLine("{0}", n); } But lets say you wanted to return the last instance of a number meeting a given criteria.  You could do something like this: public void RunSnippet() { int[] nums = {0,1,2,3}; int n = nums.Last(item => item < 3 && item > 1);  //Returns 2 Console.WriteLine("{0}", n); } It's a bit overkill for something so simple, but the concept is what's important.

  • Anonymous
    July 25, 2008
    The comment has been removed

  • Anonymous
    July 31, 2008
    I don't understand anonymous method bashing. They are extremely using in writing lean code. You don't always need resusability. I see great value in anoymous methods especially when dealing with event hanlders. For example: myButton.Click += { myButton.Enabled=false;}

  • Anonymous
    August 05, 2008
    The comment has been removed

  • Anonymous
    September 02, 2008
    Excellent article, good comments too.  Have to say i'm loving working with var and lambda's with linq, the speed at which i am now producing working performant code has near doubled, and since im maintaining the solution and documenting it i have no qualms over using this new tech, although it took a short while to get used to i feel teaching it to the jr dev's will not be as hard as teaching them anon delegates....

  • Anonymous
    January 08, 2009
    The comment has been removed

  • Anonymous
    January 28, 2009
    Excellent. I was the best article about Lambdas that I was reading so far.