Understanding Lambda Expressions


Examining the forums lately, I have noticed a lot of people have a problem getting their heads wrapped around lambda expressions. It is my belief that understanding the context and history are the key to understanding lambda expressions. So, let’s see if we can illuminate this subject.

Delegates

The first step to understanding Lambda expressions is understanding delegates. A delegate is a pointer to a function. In C# 1.x, you most often saw delegates expressed this way:

//Delegate definition
delegate void MyDelegate(int i, string s);

//method that adheres to the delegate
public void MyMethod(int i, string s) { //More here }

//Instantiating the delegate
MyDelegate a = new MyDelegate(MyMethod);

Since we are hooking the pointer up at run time, rather than straight calling the method straight out, you have the ability to dynamically hook up delegates. This is not really important for our talk, but this is why delegates are useful for events and multicasting of information. Back to our talk. By C# 2.0, Microsoft was still using delegates, but simplified the syntax a bit, as we can now do this:

//Simple instantiation
MyDelegate a = MyMethod;

By 2.0, we could also use delegates to "inject code" into a method. Or, if you want to look at this in another direction, we are creating a callback to delegated code. Before doing that, let’s look at a common refactoring:

//Before refactoring
public void EnumeratedStrings()
{
    foreach(string s in MyStringArray)
    {
         //Do the processing here
    }
}

//After refactoring
public void EnumeratedStrings()
{
    foreach(string s in MyStringArray)
    {
         ProcessString(s)
    }
}

private void ProcessString(string s)
{
    //Do the processing here

}

In this example, we started with some code in one, or more, methods and moved it into its own routine. This is done for reuse or because of a code smell (repeated code). Now, suppose we had multiple ways of processing the string. We can either write multiple methods to enumerate that call multiple methods … or we can use a delegate as input for our method and sort this out at run time. 

public delegate void MyDelegate(string s);

public void EnumeratedStrings(MyDelegate callback)
{
    foreach(string s in MyStringArray)
    {
         callback(s);
    }
}

Now, I have the ability to call the process string method, but doing this:

MyDelegate a = ProcessString;

But, I can also do something more like this:

MyDelegate b = ProcessStringDifferently;

I can also do both, if I am so inclined, but that is a tangent.

Anonymous Methods

The next step in the process is anonymous methods. In anonymous methods, we declare the delegate along with its code as we pass it into a method. It is anonymous as it is determined by context and not by wiring the delegate. This allows us to determine what code we need to run based on a our needs at the time of development.

Say, for example, we want know we want to iterate through a string array and do work on it, but the work can vary depending on what code is using the method. We might do something like this:

public class AnonMethodExample
{
    public delegate void MyDelegate(string text);
    public string[] names;

    public AnonMethodExample()
    {
        names = new string[3]{"Greg", "Henry", "Alan"};
        DoWorkOnStringArray(delegate(string name) { Console.WriteLine(name); });
    }

    void DoWorkOnStringArray(MyDelegate callback)
    {
        for(int i = 0;i<names.Length;i++)
        {
            callback(names[i]);
        }
    }
}

The output here is:

Greg
Henry
Alan

When the class is instantiated, each name is printed. But let’s say some other code just wants the length of the names along with the name. I do not have to change the method, I simply change the call.

public class AnonMethodExample2
{
    public delegate void MyDelegate(string text);
    public string[] names;

    public AnonMethodExample2()
    {
        names = new string[3] { "Greg", "Henry", "Alan" };
        DoWorkOnStringArray(delegate(string name)
        { Console.WriteLine("{0} has {1} characters", name, name.Length); });
    }

    void DoWorkOnStringArray(MyDelegate callback)
    {
        for (int i = 0; i < names.Length; i++)
        {
            callback(names[i]);
        }
    }
}

This one now outputs

Greg has 4 characters
Henry has 5 characters
Alan has 4 characters

The real power here is the DoWorkOnStringArray() can exist in a different class or even a different library, allowing the calling application to determine what it wants to happen at runtime. Of course, in the real world, the DoWorkOnStringArray() method would do something more than simply call the delegate code from the anonymous method.

This method is obviously not anonymous from the calling application, but it is called anonymous as the called method does not know what code it is running until it runs it (essentially). I can still explicitly declare the delegate and pass it, but anonymous method syntax allows me to determine the code as I write it. If you do find you are injecting the same code from two places, you should refactor to a more traditional delegate syntax, as it encourage reuse, but the anonymous method allows you have the same routine perform work in different ways. If you want a more real world implementation, you should read Dan Wahlin’s blog entry on the subject, called the Power of Anonymous Methods.

Generics

Before moving to lambda expressions, we have one more concept that is also from C# 2.0: Generics. Let’s go back a few years ago to .NET 1.x. In order to express something "generically" we had to box as an object. For example:

object[] os = new object[2];
int i = 1;
string s = "one";

os[0] = i;
os[1] = s;

As all "objects" in .NET stem from System.Object, we can generically refer to every object as System.Object. That is why the above code works. This lead us to creating lists and dictionaries, etc. that boxed to an object, even when we had a specific type. For example, suppose we use an integer for a key and a double for the value. In .NET 1.x, both would be objects. The danger here comes when we do something like so:

dict.Add(1, "this should be a number");

It blows up on the way out, rather than fire an exception when loaded, as it should:

double val = dict[1];

With 2.0, we have the ability to use Dictionary<TKey, TValue> to set our types. This saves us, as it will catch the error earlier in the cycle:

Dictionary<int, double> dict = new Dictionary<int, double>();

//Will not compile because of this line
dict.Add(1, "this should be a number");

The benefit here is I can create multiple strongly typed implementations off a single generic, rather than repeating the same code for each type that could possibly need the generic, whether it be a list, dictionary or otherwise.

Lambda Expressions

A Lambda expression is a shortened syntax for an anonymous method. Since I am running out of time, I will use an example from the book Introducing LINQ (MSPress), which you can download for free. It starts with the concept of an Aggregate generic and a generic delegate.

public class AggDelegate
{
    public List<int> Values;
    delegate T Func<T>(T a, T b);
    static T Aggregate<T>(List<T> l, Func<T> f)
    {
        T result = default(T);
        bool firstLoop = true;
        foreach (T value in l)
        {
            if (firstLoop)
            {
                result = value;
                firstLoop = false;
            }
            else
            {
                result = f(result, value);
            }
        }
        return result;
    }
}

The example then uses an anonymous method to sum the values of the list, by passing in the delegate (int a, int b) { return a + b; } to the Aggregate. Here is the example:

public static void Demo()
{
    AggDelegate l = new AggDelegate();
    int sum;
    sum = Aggregate(
    l.Values,
    delegate(int a, int b) { return a + b; }
    );
    Console.WriteLine("Sum = {0}", sum);
}

If you put back up to the AggDelegate definition, you will see that it is taking the entire list and aggregating it. On the first loop, it sets the result to the first value. From there is keeps adding the value using the injected sum method. To move this to lambda expression, we simple drop the word delegate and add the pointer to the code being run for the values expressed. Lets compare:

//Original
delegate(int a, int b) { return a + b; }

//Explicitly typed variables in a lambda expression
(int a, int b) => { return a + b; }

We can further simplify this by implicitly typing the input variables:

//Implicitly type variables in a lambda expression
(a, b) => { return a + b; }

Now, since we know that we are returning values with a lambda expression, we can take this a bit farther and not include the method definition in the format of {} or include the return keyword. This looks like this:

//Lambda Expression
(a, b) => a + b

Here is the final journey. Each of these statements declares the same thing:

//Original
delegate(int a, int b) { return a + b; }

//Explicitly typed variables in a lambda expression
(int a, int b) => { return a + b; }

//Implicitly type variables in a lambda expression
(a, b) => { return a + b; }

//Lambda Expression
(a, b) => a + b

Pretty kewl, eh? Hopefully now, you see that Lambda expressions are not really that complex after all. They are just shorthand for things you already know how to use, at least on a theoretical level. One more snippet from the book Introducing LINQ. This one shows all of the ways of expressing in Lambda (cute play on words):

( int a, int b ) => { return a + b; }       // Explicitly typed, statement body
( int a, int b ) => a + b;                  // Explicitly typed, expression body
( a, b ) => { return a + b; }               // Implicitly typed, statement body
( a, b ) => a + b                           // Implicitly typed, expression body
( x ) => sum += x                           // Single parameter with parentheses
x => sum += x                               // Single parameter no parentheses
() => sum + 1                               // No parameters

In my next blog entry on this series, I will take this a step farther and go into extension methods, as delegates, in the form of anonymous methods/lambda expressions are the key for understanding the hows and whys of extension methods.

Peace and Grace,
Greg

Advertisements

One Response to Understanding Lambda Expressions

  1. Unknown says:

    wow gold!All wow gold US Server 24.99$/1000G on sell! Cheap wow gold,wow gold,wow gold,Buy Cheapest/Safe/Fast WoW US EU wow gold Power leveling wow gold from the time you wWorld of Warcraft gold ordered!

    wow power leveling wow power leveling power leveling wow power leveling wow powerleveling wow power levelingcheap wow power leveling wow power leveling buy wow power leveling wow power leveling buy power leveling wow power leveling cheap power leveling wow power leveling wow power leveling wow power leveling wow powerleveling wow power leveling power leveling wow power leveling wow powerleveling wow power leveling buy rolex cheap rolex wow gold wow gold wow gold wow gold -35826062086822

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: