Lambda, Lambda, Lambda!

Published 18 January 07 02:47 PM

When I first heard about lambdas, I was highly skeptical primarily because I haven't seen or thought about these things since my computer science classes in college. If you need further evidence for my general skepticism, please see the definition of lambda expressions on Wikipedia. However, I was forced to look at them while researching the Linq project, so I decided to spend a bit more time with them and have a deeper look. And as it is with so many things, the more I surrounded myself with them, the more I kind of started to like them. Therefore, I'll spend a few seconds going over how I see them and you can use them.

At the core, a lambda expression is simply a function. You give it inputs, it gives you output. These expressions can then be linked together to build very complex programs. For the mind-numbing explanation, please see the Wikipedia article listed above. Anyways, in .NET terminology a lambda expression is a function that is fulfills the signature requirements specified by a delegate. For a little refresher, consider the following delegate that is defined in the .NET Framework 2.0.

public delegate bool Predicate<T> (T obj)

By the way, if you haven't gotten used to generics yet, I would start now as use of them is dramatically increased in the .NET Framework 3.5. Anyways, the predicate delegate is used by standard .NET collection classes to enable searching by arbitrary criteria. For example, let's say I define the class Person as follows.

public class Person { 
  public Person(string name, int age) { 
    Name = name; 
    Age = age; 
  } 

  public string Name; 
  public int Age; 
}

If I create a list of Person objects, I can filter that list to find all people with an age greater than 10 by creating an instance of the Predicate delegate and passing it to the FindAll method on my List class. The code would look as follows.

   1:  public static void Main() { 
   2:    List<Person> people = new List<Person>(); 
   3:    people.Add(new Person("howard", 29)); 
   4:    people.Add(new Person("jennifer", 30)); 
   5:    people.Add(new Person("hannah", 8)); 
   6:   
   7:    List<Person> oldPeople = people.FindAll(ageGT10Filter); 
   8:   
   9:    foreach (Person p in oldPeople) 
  10:      Console.WriteLine(p.Name); 
  11:  } 
  12:   
  13:  static bool ageGT10Filter(Person p) { 
  14:    return p.Age > 10; 
  15:  }

When the FindAll method is called (line 7), it iterates through my List object and calls my delegate instance (ageGT10Filter - line 13), passing it the Person object at that point in the list. If the delegate instance returns true, that Person object is added to the list returned by FindAll.

C# 2.0 extended this pattern of delegate passing with the introduction of anonymous methods (therefore, when looking at lambdas, I would expect that the concept will initially seem more familiar to C# developers as anonymous methods were not supported by VB.NET in VS 2005). By using anonymous methods, the code block above would look more like the following.

   1:  public static void Main() { 
   2:    List<Person> people = new List<Person>(); 
   3:    people.Add(new Person("howard", 29)); 
   4:    people.Add(new Person("jennifer", 30)); 
   5:    people.Add(new Person("hannah", 8)); 
   6:   
   7:    List<Person> oldPeople = people.FindAll(delegate(Person p) 
   8:      { return p.Age > 10; }
   9:    ); 
  10:   
  11:    foreach (Person p in oldPeople) 
  12:      Console.WriteLine(p.Name); 
  13:  }

As you can see, the delegate method instance is replaced by an inline method declaration (lines 7-9). This provides 2 major benefits to the previous approach: 1) fewer methods and smaller code, and 2) the constant declaration of 10 could be replaced with a user supplied value such as the following.

List<Person> oldPeople = people.FindAll(delegate(Person p) 
  { return p.Age > iValue; }); 

So now let's look at this implementation in C# 3.0 using lambda expressions.

List<Person> oldPeople = people.FindAll(p => p.Age > 10);

The first thing that I want to point out here is that the argument required by the FindAll method has not changed at all – it still requires a Predicate delegate instance. The lambda in this context is used as a shorthand way of defining the delegate instance. If we then look at the structure of the lambda expression itself, we'll see that it's really very simple.

p

=>

p.Age > 10

inputs (delegate parameters)

Lambda operator

Expression to be evaluated – expression return must match delegate return value type

 

Now, at this point, you're probably saying, "big deal – this is just a slightly shorter way to do something that I could already do in C# 2.0" – and if this was the only benefit that lambdas brought, you would be correct. However, there is something much, much cooler about lambdas in C# 3.0. This is that they can be treated as data and reinterpreted by other code (metaprogramming anyone?).

Rather than delve into an esoteric description, let's just see an example. Say that I wanted to be able to develop a generic query language – one that would be based on native C# code, but could be translated to all sorts of data sources, like SQL Server (starting to sound familiar??). For example, we would want our database query to look just like our previous in-memory query.

SQLEntityProvider.FindAll<Person>(p => p.Age > 10);

For the purposes of this example, we're going to assume a TON about the mapping relationship between the object and the database. Our generic provider might look something like the following.

   1:  public static IEnumerable<T> FindAll<T>(
   2:    Expression<Predicate<T>> filterExp) {
   3:   
   4:    StringBuilder sb = new StringBuilder(); 
   5:    sb.Append("select * from tbl" + typeof(T).Name); 
   6:    sb.Append(" where "); 
   7:   
   8:    BinaryExpression expBody = filterExp.Body as BinaryExpression; 
   9:   
  10:    if (expBody.Left.NodeType == ExpressionType.MemberAccess) 
  11:      sb.Append(((MemberExpression)expBody.Left).Member.Name); 
  12:   
  13:    switch (expBody.NodeType) { 
  14:      case ExpressionType.GreaterThan: 
  15:        sb.Append(" > "); 
  16:        break; 
  17:      case ExpressionType.LessThan: 
  18:        sb.Append(" < "); 
  19:        break; 
  20:      case ExpressionType.Equal: 
  21:        sb.Append(" = "); 
  22:        break; 
  23:      case ExpressionType.NotEqual: 
  24:        sb.Append(" <> "); 
  25:        break; 
  26:    } 
  27:   
  28:    if (expBody.Right.NodeType == ExpressionType.Constant) 
  29:      sb.Append(((ConstantExpression)expBody.Right).Value); 
  30:   
  31:    Console.WriteLine(sb.ToString()); 
  32:   
  33:    return null; 
  34:  }

When the program is run and we examine the console output, we see the following.

select * from tblPerson where Age > 10

Now when you step back and think about it, this is actually REALLY cool – and what enables this coolness is the fact that lambda expressions, in addition to being directly executable, can also be treated as data. This is what enabled me to look through the expression tree and convert the lambda expression elements into equivalent SQL statement elements. So how do you control whether your lambda expression is treated as code (delegate) or as data? Simple – either pass it to a parameter expecting a delegate instance (as in my first case) OR pass it to a parameter expecting a object of type Expression<TDelegate>. The Expression generic type has special meaning in C# 3.0 and parses the lambda expression into a data structure rather than generating IL executable code.

So now that you have a general idea of how this new language feature works, what can you do with it? As is always the case – whatever you want! Personally, I would love to see somebody write an extension to NHibernate to allow use of Linq queries in the place of HQL – so if anyone's looking down that path, please let me know! However, even if you're not running to the bleeding edge to start implementing your own lambda based solutions, I hope that this article has given you just a bit of insight into how Linq works, and how we are just scratching the surface on what we can do with this powerful new language feature!

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# Fear and Loathing said on January 18, 2007 8:00 PM:

Actually this post has nothing to do with llamas but Flickr never fails me: I've been personally putting

# Ryan Anderson said on January 19, 2007 12:33 AM:

You have no idea how crystal clear you just made things for me... Now that is a post!

THANK YOU!!!

# Glenn Block said on January 20, 2007 2:32 AM:

Great article Howard.

So let me get this right. You mean I can even write my own find function that would take C# code and translate it into say XSLT? That sounds really cool! Wait, the LINQ guys already thought of it...it's called XLINQ. :)

# Certifications and Software Development said on February 9, 2007 5:05 PM:

A while back I wrote a post about lambda expressions in C# 3.0 and how they are one of the enabling technologies

# Certifications and Software Development said on April 9, 2007 8:42 PM:

A while back, I posted on lambda expressions in C# 3.0 . In that post, I concluded by saying the following.

# dotnetjeff said on April 18, 2007 3:01 PM:

wow! i have been schooled.

# Vaibhav, said on June 19, 2007 6:24 AM:

gud article,

Expression(Lambda) => LINQ

# smguest said on October 6, 2007 11:08 PM:

Excellent post - thanks Howard!

# DotNetKicks.com said on March 18, 2008 7:53 PM:

You've been kicked (a good thing) - Trackback from DotNetKicks.com

# Rick's Development Wonderland said on March 20, 2008 10:58 AM:

Howard Dierking over at MSDN has a really interesting Blog post entitled " Lambda, Lambda, Lambda! "

# randy said on March 22, 2008 8:21 PM:

Absolutely great description of lambda's.  You are a gift to developerkind.

# Roy Clemmons said on March 25, 2008 3:22 PM:

This is the first post of many that I've read that clearly described Lambda expressions for the beginner. I've read all the popular guru's blogs, and while they have useful information, I always left their sites missing a vital piece of information that prevented complete comprehension. You've provided that piece and everything falls into place now. I'm now a convert. love lambdas! Thank you.

# Aman said on April 3, 2008 3:32 AM:

Nice Lambda, Lambda,Lambdaaaaaaaaaa.

Really Great job

# Andrew Hay's Blog said on April 16, 2008 3:57 PM:

Since I&#39;m posting this in advance, I hope my session titled Understanding LINQ was a huge hit and

# hiti said on September 16, 2008 11:59 AM:

thanks for a great article. phew, i can start understanding thing things now.

# Gandhi said on November 25, 2008 5:09 AM:

Lambdas prevent Edit-and-Continue feature in Visual Studio, if you edit method which contains lambda, even if edition is not related with lambda.

# Code Monkey Labs said on February 22, 2009 10:51 PM:

Looking for a bit of gadget pr0n? Check out IronKey - the coolest, most secure USB key on the planet! General Lambda, Lambda, Lambda : Howard Dierking gives a nice overview of Lambda functions, one of the many new language features in C# 3.0. Introduction

# Howard Dierking said on March 4, 2009 11:17 AM:

I wanted to at least say a quick hello before diving in.&#160; That said, I will be brief and simply

# Community Blogs said on March 4, 2009 11:31 AM:

I wanted to at least say a quick hello before diving in.&#160; That said, I will be brief and simply

# Community Blogs said on March 7, 2009 8:48 PM:

I wanted to at least say a quick hello before diving in.&#160; That said, I will be brief and simply

Leave a Comment

(required) 
(optional)
(required) 

  
Enter Code Here: Required

About hdierking

I am currently the Editor-in-Chief for MSDN Magazine. I joined Microsoft in 2006 as a product planner with the certification team at Microsoft Learning. Prior to that, I spent my career as a developer and later as an architect. My main technology passions include pretty much anything on language theory, agile development, and service-oriented architecture.
Page view tracker