Variations on a Func-y theme


Delegates have come a long way since C# 1.0 debuted back in early 2002.  The progression from using delegate parameters (outside of events) grew from quite cumbersome to fairly expressive with Lambda expressions with C# 3.0.  Functional style programming has become easier (some say feasible) with generics, lambdas and expression trees.

But it wasn’t always quite so easy.  To fully appreciate the possibilities a functional-style API gives us, I like to crack open the history books and examine how delegates have changed in C# throughout the years.

Manual class creation: C# 1.0

Suppose I want to search a list of books by author and return the first matching book.  For these examples, I’ll assume I have the full Enumerable extensions, but search methods could be created manually by declaring special delegates.  But for simplicity’s sake, I’ll just use the FirstOrDefault(TSource) extension method.

In C# 1.0, I had to create a special class to perform the matching:

public Book FindByAuthor(string author)
{
    List<Book> books = GetBooks();

    MatchesAuthorSpecification spec = new MatchesAuthorSpecification(author);

    return books.FirstOrDefault(spec.IsSatisfiedBy);
}

After getting the list of books, I create a MatchesAuthorSpecification, initializing the spec object with the author I’m trying to find.  Here’s the specification class:

public class MatchesAuthorSpecification
{
    private readonly string _author;

    public MatchesAuthorSpecification(string author)
    {
        _author = author;
    }

    public bool IsSatisfiedBy(Book book)
    {
        return book.Author == _author;
    }
}

The FirstOrDefault method expects a delegate to perform the matching, specifically a Func<TSource, bool> predicate.  All this means is that it needs a method that takes a Book and returns a bool.  The “IsSatisfiedBy” method matches that signature, so my little Specification class works to perform the author matching.

The Specification pattern is great, but if I don’t need that class outside of this FindByAuthor method, it’s not doing much for me.  With C# 1.0, I didn’t have a choice in the matter, I have to pass in a concrete method name to the FirstOrDefault method.

Anonymous methods on the move: C# 2.0

With C# 2.0 came anonymous methods.  I could now create little method snippets inside other methods, without needing to create special classes or other methods to hold the logic.

Additionally, these anonymous methods supported closures, which allow anonymous methods to reference the scope they were created in.  This allows me to reference local variables inside the method block, with a bunch of compiler magic taking care of the rest.

Using anonymous methods, my FindByAuthor method now looks like:

public Book FindByAuthor(string author)
{
    List<Book> books = GetBooks();

    return books.FirstOrDefault(delegate(Book book) { return book.Author == author; });
}

The body of the anonymous method looks exactly like the C# 1.0 version, without the need to create a class around it.  Note that the anonymous method refers to the “author” variable passed in to the method.  The anonymous method “remembers” what the local author variable is when the anonymous method is declared.  This allows the Find method to work properly.

Anonymous delegates were a nice addition, but they could get ugly fairly quickly.  You never wanted more than two or three lines in these anonymous methods, but even one line didn’t look great.  It’s hard to discern exactly what’s going on with all of the braces, keywords and statements around.

Lambda expressions: C# 3.0

Lambda expressions are finally available with C# 3.0.  Lambda expressions offer a much terser syntax for creating anonymous methods.  But it turns out there are two types of lambdas:

  • Statement lambdas
  • Expression lambdas

Statement lambdas look almost exactly like anonymous methods, with the “delegate” keyword removed:

public Book FindByAuthor(string author)
{
    List<Book> books = GetBooks();

    return books.FirstOrDefault(book => { return book.Author == author; });
}

Lambda expressions also include the lambda operator “=>” to associate the statement or expression block with the input parameters.  In the above example, the type of “book” is inferred from the underlying delegate type (Func<Book, bool>).  This format is an improvement on anonymous methods, but the statement block still looks strange inside another method call.

With expression lambdas, I can make the syntax about as terse as it gets:

public Book FindByAuthor(string author)
{
    List<Book> books = GetBooks();

    return books.FirstOrDefault(book => book.Author == author);
}

Now that’s readable!  No extra class to define, just a short lambda expression describing the matching function.  The compiler can look at the expression lambda and infer the operation based on its result.  Since the expression shown returns a boolean, the C# compiler infers that this is what the method should return.

Looking at each implementation in .NET Reflector, each implementation from the manual class creation to lambda expression is almost exactly the same.

###

Lambdas are your friend

Delegates have come a long way since C# 1.0, with C# 3.0 allowing powerful functional programming concepts like closures, currying, folding and others.

Entire algorithms can be abstracted and the varying parts passed in as delegates.  The entire System.Linq.Enumerable set of extension methods is based on this concept of extracting algorithms.  With the simplicity of lambda expressions, new avenues of removing duplication are opened up.

Is your process dead?