Expressions, Delegates, and Lambdas, Oh My!

C# 3.0 heralded a new era of syntax candy that was introduced in a large part to support LINQ.  The new syntax also brings expanded freedom of expression for developers, however by increasing the number of ways in which similar intentions may be expressed there is additional opportunity for confusion.  In order to alleviate some of this confusion I decided to write about some of these new approaches and provide a reference for understanding how they relate to one another.

Anonymous Methods
First up on the chopping block are anonymous methods.  Let’s look at why these are cool and where you might expect to encounter them in code.  Prior to .NET 2.0, the only way to declare a delegate was using named methods.  If you aren’t familiar with the concept of delegates, I highly recommend you read this MSDN article before proceeding.  It will bring you up to speed on new world function pointers and make the rest of the article much more useful.  Assuming familiarity with old-school delegates, consider the following example of a named delegate and then an equivalent anonymous method and how the two are related.

This example shows a standard delegate definition, a method whose signature matches that delegate, and then a class that uses a delegate of that shape.  Then we’ll look at how an anonymous delegate could be used to achieve the same thing.

First we declare the delegate shape:

public delegate string UsefulDelegate(int inputA, DateTime inputB);

Then we declare a method that adheres to that shape:

public string DoSomethingUseful(int num, DateTime dateTime)
{
return String.Format(“[Worker 1] Value {0} processed at {1}”, num, dateTime);
}

Finally there is the consumer of this delegate:

public void ManageSomething(UsefulDelegate worker, int num)
{
int someValue = 5;
worker.Invoke(someValue, DateTime.Now);
}

This is a pretty boring example but it will suffice to get the point across.  Now imagine that you are handed a dll that contains the ManageSomething method.  You’re required to use this method as it represents a common point in the application you’re integrating with.  Rather than defining a delegate shape and implementation method, wouldn’t it be nice to just pass in the contents of the delegate?  That’s precisely what an anonymous method allows you to do.  Consider the following implementation:

ManageSomething(delegate(int A, DateTime B) { return String.Format(“{0}: {1}”, A, B); });

That’s pretty slick!  In this rather contrived case we have abstracted a lot of the considerations that would lead you to choose an anonymous delegate over an explicitly defined version.  One good case for using an anonymous delegate is if your application will only implement one method of that shape.  Why formally define a delegate to constrain the signature of only one implemented method?  Let’s consider the reason delegates exist in the first place.  They constrain the signature of a method such that another class can invoke that method safe in the knowledge that the parameter and return types for that method will conform to its needs.  However, if we are only going to ever declare a single implementation then it’s really nice to place that implementation inline and avoid the wastage of a delegate definition.

Still, that whole delegate() { } stuff wrapping the actual code performing the work seems a little bit long-winded don’t you think?  This is new C# 3.0 Hyper Jet-Filtered Edition after all!

Lambda Expressions
Enter the lambda expression.  A lambda expression is an anonymous function that can be used to create a delegate or expression tree.  Hmm, that sounds impressive, but what does it mean?  What it means is that you can remove the new delegate() { } stuff and replace it with the operator =>.  This new operator is read as “goes to” and roughly means “the parameters on the left side of => are fed as parameters to the function on the right.”  Let’s look at this in the context of our ManageSomething() method call.  Using a lambda expression this could be re-written as:

ManageSomething((int A, DateTime B) => return String.Format(“[Worker 3] Value {0} processed at {1}”, B, A));

The left-hand side of the expression specifies the parameters that will be supplied and the right-hand side of the expression contains the function body.  Hurrah!  The need for the delegate, return and { } have disappeared!  That is not all that lambda expressions bring as they can also be used to build expression trees, but that discussion is best left for another post.  Suffice to say for now that these are three different forms for expressing the same intent.  The compiler will sort out the rest.

The purpose of this post is not to dive into the finer points of named methods, delegates, anonymous methods, or lambdas, rather to give a first look at the three forms to pave the way for a more in-depth investigation.

Example Listing
For those of you who use snippet compiler, here is a sample listing that can be directly pasted in and tweaked:

using System;
using System.Collections.Generic;

public class MyClass
{
public static void RunSnippet()
{
Manager m = new Manager();

// First example  (defined delegate)
m.ManageSomething(DoSomethingUseful);

// Second example (anonymous method)
m.ManageSomething(delegate(int A, DateTime B) { return String.Format(“[Worker 2] Value {0} processed at {1}”, A, B); });

// Third example (lambda expression)
m.ManageSomething((int A, DateTime B) => String.Format(“[Worker 3] Value {0} processed at {1}”, A, B));
}

public static string DoSomethingUseful(int num, DateTime dateTime)
{
return String.Format(“[Worker 1] Value {0} processed at {1}”, num, dateTime);
}

private class Manager
{
public delegate string UsefulDelegate(int inputA, DateTime inputB);

public void ManageSomething(UsefulDelegate worker)
{
int someValue = 5;
Console.WriteLine(worker.DynamicInvoke(someValue, DateTime.Now));
}
}

#region Helper methods

public static void Main()
{
try
{
RunSnippet();
}
catch (Exception e)
{
string error = string.Format(“—\nThe following error occurred while executing the snippet:\n{0}\n—“, e.ToString());
Console.WriteLine(error);
}
finally
{
Console.Write(“Press any key to continue…”);
Console.ReadKey();
}
}

private static void WL(object text, params object[] args)
{
Console.WriteLine(text.ToString(), args);
}

private static void RL()
{
Console.ReadLine();
}

private static void Break()
{
System.Diagnostics.Debugger.Break();
}

#endregion
}
This entry was posted in Technology, Uncategorized and tagged , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *