Demystifying C# 3.0 - Part 4: Lambda Expressions

Posted on 6/30/2006 @ 8:43 PM in #Vanilla .NET by | Feedback | 26107 views

In the Demystifying C# 3.0, I've already talked about -

a) Demystifying C# 3.0 - Part 1: Implicitly Typed Local Variables "var"
b) Demystifying C# 3.0 - Part 2: Anonymous Types
c) Demystifying C# 3.0 - Part 3: Extension Methods

Lets next talk about Lambda Expressions.

The best way I can describe Lambda Expressions are - C# Anonymous Methods, only a lot cooler.

So what is an anonymous method in C# 2.0?

Well y'know you can write code like below in C# 1.x/2.x -

class SomeClass
{
   delegate void SomeDelegate();
   public void InvokeMethod()
   {
      SomeDelegate del = new SomeDelegate(SomeMethod);
      del();
   }

   void SomeMethod()  
   {     
     
Console.WriteLine("Hello");
   }
}

Well, anonymous methods let you write the above code in a much more "convenient to write terse way" as below -

class SomeClass
{
    delegate void SomeDelegate();
    public void InvokeMethod()
    {
        SomeDelegate del = delegate()
                           {
                              
Console.WriteLine("Hello");
                           };
        del();
    }
}

Wunner'ful. What you see above after the "delegate()" stuff is an anonymous method. Even though it's better than the C# 1.x version, it is still quite verbose.

Lambda expressions give you an even more concise, functional syntax using the"=>" token. In fact, the above could now simply be written as

class SomeClass
{
    delegate void SomeDelegate();
    public void InvokeMethod()
    {
        SomeDelegate del = () => Console.WriteLine("Hello") ;
        del();
    }
}
 

In general, the syntax is

parameters => expression

Now obviously, even though this is terse, frankly I don't know how you feel about it - I think changing syntax just for the heck of it, dude that's so not cool!! Now it turns out, Lambda expressions aren't simply a cool new way of writing anonymous methods. They are in fact, a functional superset of anonymous methods. Why?

  • Lambda expressions can "infer" parameter types, even if you omit them. Anonymous methods bitch and moan if you miss out any parameter explicitly.
  • Lambda expressions can use both statement blocks and expressions. Anonymous methods take only statement blocks - thus making lambda expressions a tad bit more convenient to use.
  • Lambda expressions can be passed in as arguments. And when you do so, they participate in "Type argument inference" and "method overload resolution" - (WHOAA what are these 2 heavy duty sounding words? - Sit tight, we'll talk about them soon-ish).
  • Lambda expressions with an expression body can be converted into expression trees. Expression trees is .. well, we haven't discussed that yet ~ so we'll discuss that, when I write a blogpost about Expression Trees.

Lets talk about these one by one -

Lambda expressions can "infer" parameter types, even if you omit them. Anonymous methods bitch and moan if you miss out any parameter explicitly.

Heavy duty words for something as simple as -

(int x) => x + 1 ; // is the same as
x => x + 1 ;

In the second instance, the type is being inferred.

Lambda expressions can use both statement blocks and expressions. Anonymous methods take only statement blocks - thus making lambda expressions a tad bit more convenient to use.

Again, heavy duty words for something as simple as -

(int x) => x + 1 ; // is the same as
(int x) => { return x + 1; }

In the second instance, we have a statement block, and in the first we have an expression. So the first - is the same as the below, but it is terse and easier to write and understand.

Lambda expressions can be passed in as arguments. And when you do so, they participate in "Type argument inference" and "method overload resolution"

You could pass in Lambda expressions as arguments into a generic method. Lets consider this in an example. Lets say, you had an extension method (what are extension methods?) as shown below -

public static class myExtensions
{
    public static void SpankIt<T,U>(
        this IEnumerable<T> source,
        Func<T, U> someParameter)
    {
        foreach (T element in source)
           Console.WriteLine("SPANK " + someParameter(element) + "!!");
    }

}

You could use the above Extension method a bit like this -

static void Main(string[] args)
{
    List<String> whoToSpank = new List<String>() ;
    whoToSpank.Add("Monkey") ;
    whoToSpank.Add("Bannana") ;

    whoToSpank.SpankIt(c => "Monkey") ;
}
 

And this produces "SPANK MONKEY !!" twice.

But wait a minute. What is "U" in the extension method? If you put a breakpoint in your extension method, you will find out that C# 3.0 is smart enough to infer that "U" is a string. Dude, you never said "String" it "inferred" it based upon what lambda expression you passed in.

You can't do that with anonymous methods, now can you?

Overload resolution is very similar to this, so I will skip explaining that. And yes there are clear laid out rules regarding overload resolution and type-inference, refer to the C# specs for that (I hate regurgitating docs).

Finally -

Lambda expressions with an expression body can be converted into expression trees.

Expression trees is well just read this post instead.

 

Sound off but keep it civil:

Older comments..


On 7/10/2006 9:19:10 PM Weifen Luo said ..
For the SpankIt sample, please include the definition of delegate Func for better understanding:

delegate U Func<T, U> (T arg);


On 11/6/2007 9:23:46 AM Dragan Sretenovic said ..
Sahil

There is a problem with printing these pages,


only first page gets printed, both from IE7 and Firefox2

Thank you for excellent description of new C# features :)

Dragan


On 4/17/2008 4:15:49 AM Sebi said ..
Good explanation (like the one on extension methods). thanks for that.


should't the output of the last example be


"Spank Monkey!! Spank Bannana!!"?


On 5/12/2008 3:35:41 PM D said ..
@Sebi,

It goes like this:

1: Console.Writeline in SpankIt writes "SPANK " + someParameter(element)


2: The foreach iterates through source (source being our list of strings) and will thus execute:

Console.WriteLine("SPANK " + someParameter("Monkey"));


Console.WriteLine("SPANK " + someParameter("Banana"));

3: someParameter is the second parameter of the extension method, hence the parameter passed when invoking SpankIt: whoToSpank.SpankIt(c => "Monkey")


4: someParameter is our lambda expression


5: Look at the lambda expression: c => "Monkey". Knowing that the syntax is parameters => expression, we see that c is the inparameter, (in our invokations, first "Monkey" then "Banana"), and the rest is what happens with it: it is completely disregarded. The expression could be rewritten into a function

function someParameter(c) { return "Monkey"; }

now someParameter("Monkey") and someParameter("Banana") both return "Monkey"


On 6/6/2008 4:24:22 PM JAS said ..
I disagree that the "terse" lambda expressions are easier to understand. Also, VS's auto complete features fly in the face of the "easier to write" arguments. I HATE lambdas. We are going backward with this. Do you realize that the LINQ lambda expression functionality is actually implemented using basic methods and overlaods, and including anonymus methods. Annonymous I can handle, since at least all types and such are right there. No magic decoder ring required. The first thing that comes to mind are RegEx's. They are powerfull too, but they are extremely terse and therefore most ppl don't use them. I am considered a Regex guru in my office only because I happen to have a good Regex reference bookmarked.

I believe LamEx's are in the same category, only worse since the "standard OO" syntax anf functionality is available. I hope this is only a fad.


On 7/30/2008 10:16:34 AM hmmm said ..
You might want to look up what it means to spank a monkey...


On 7/30/2008 10:49:31 AM Sahil Malik said ..
LOL


On 1/12/2009 3:19:11 PM Jason said ..
I have to agree with JAS, this feels like a step backward toward the type sloppiness of scripting languages. Contrary to the beliefs of the creators of this feature, most people like having things specifically defined, rather than inferred. Makes understanding other people's code much easier when all types are explicit. Imagine the confusion of coming across and trying to debug a massive pile of this kind of code written by someone else. OUCH.


On 5/10/2009 11:15:03 PM Lee said ..
I disagree on your last part where

whoToSpank.SpankIt(c => "Monkey");

cannot be rewritten using Anonymous methods. In actual fact, you can:

whoToSpank.SpankIt(delegate(string c) { return "Monkey"; });

Anonymous methods also take part in inferred return values (but not parameter types). ;)


On 6/3/2010 2:38:36 PM yosefus said ..
will this example work with popping cherries too?


On 6/3/2010 4:51:20 PM Sahil Malik said ..
Yosefus - Pretty sure it will, y'know C# is pretty twisted.


On 11/24/2010 1:30:40 PM Mark Zak said ..
I rewrote the call to the extension method, to show how the lambda expression can take in the elements from the extension method.

It helped me understand how things work a bit better.

List<String> whoToSpank = new List<String>();


whoToSpank.Add("Monkey");


whoToSpank.Add("Bannana");

whoToSpank.SpankIt(c => "my " + c);


On 12/22/2010 4:04:27 PM Dylan Phillips said ..
So today I came across the following Lambda in Microsoft's Documentation for Azure Development:

// Code that runs on application startup


CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>


{


configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));


});

I have no idea how this set of calls does work, and I'm no dufus! Really frustrating that the API uses this Lambda without documentation as to what's happening under the covers. Now I have to spend hours trying to figure it out!


On 12/21/2011 8:19:15 PM Jason said ..
Spank monkey ... LOL! Good explanation, but I was more looking for other parts of lambda such as:

.Select(, select, from, where, .Where( and into, etc