Tuesday, February 7, 2012

LINQ and Lambda in C# 4.0 for cleaner, leaner code

There are times when less is more. If you want clean and concise code, or if readability is more important than finely tuned high-performance, then LINQ and Lambda expressions might just be for you.

First let's look at a small code sample that does NOT use LINQ or Lambda that where we want to filter through some physics collision results for objects of a certain ID, and then sort our results by the lower IDs first.

struct CollisionPair : IComparable, IComparer
{
    public int first;
    public int second;

    // Since we're sorting we'll need to write our own Comparer
    int IComparer.Compare( object one, object two )
    {
        CollisionPair pairOne = (CollisionPair)one;
        CollisionPair pairTwo = (CollisionPair)two;

        if (pairOne.first < pairTwo.first)
            return -1;
        else if (pairTwo.first < pairOne.first)
            return 1;
        else
            return 0;
    }

    // ...and our own comparable
    int IComparable.CompareTo( object two )
    {
        CollisionPair pairTwo = (CollisionPair)two;

        if (this.first < pairTwo.first)
            return -1;
        else if (pairTwo.first < this.first)
            return 1;
        else
            return 0;
    }
}

static void Main( string[] args )
{           
    List&ltCollisionPair&gt collisions = new List&ltCollisionPair&gt
    {
        new CollisionPair { first = 1, second = 5 },
        new CollisionPair { first = 2, second = 3 },
        new CollisionPair { first = 5, second = 4 }
    };

    List&ltCollisionPair&gt sortedCollisionsWithFive = new List&ltCollisionPair&gt();
    foreach (CollisionPair c in collisions)
    {
        if (c.first == 5 || c.second == 5)
        {
            sortedCollisionsWithFive.Add(c);
        }
    }
    sortedCollisionsWithFive.Sort();

    foreach (CollisionPair c in sortedCollisionsWithFive)
    {
        Console.WriteLine("Collision between " + c.first +
                          "and " + c.second);
    }
}
Now let's take a look at what we can do when we use LINQ and Lambda expressions to clean things up a bit.
struct CollisionPair
{
    public int first;
    public int second;
}

static void Main( string[] args )
{           
    List&ltCollisionPair&gt collisions = new List&ltCollisionPair&gt
    {
        new CollisionPair { first = 1, second = 5 },
        new CollisionPair { first = 2, second = 3 },
        new CollisionPair { first = 5, second = 4 }
    };

    (from c in collisions 
    where ( c.first == 5 || c.second == 5 )
    orderby c.first select c).ForEach(c =&gt
        Console.WriteLine("Collision between " + c.first +
                          "and " + c.second));
}
So which part is LINQ, and which part is Lambda? Here's the LINQ part:
(from c in collisions 
    where ( c.first == 5 || c.second == 5 )
    orderby c.first select c)
This loops through 'collisions', and you can reference each element in the container by 'c'. Then it filters the list so only elements with either a first or second ID of 5 remain. Then it orders the list by the first element, and then selects all elements remaining and creates a list out of them. In this case the list may be hard to see, it's formed by wrapping the entire LINQ expression in parentheses. The sample below might make it easier to see:
// LINQ
var filteredCollisions = from c in collisions 
    where ( c.first == 5 || c.second == 5 )
    orderby c.first select c
And here is the Lambda expression:
// Lambda
ForEach(c =&gt
    Console.WriteLine("Collision between " + c.first +
                      "and " + c.second));
This performs a ForEach on a list, the list formed by the LINQ expression that comes before it in the original code. For each element in the list, it calls each element 'c', and then performs a Console.Writeline on each element. Here's the original sample, simplified a bit:
// LINQ
var filteredCollisions = from c in collisions 
    where ( c.first == 5 || c.second == 5 )
    orderby c.first select c

// Lambda
ForEach(c =&gt
    Console.WriteLine("Collision between " + c.first +
                      "and " + c.second));
If you want to use LINQ you'll want to make sure to use System.Linq:
using System.Linq;

No comments:

Post a Comment