C# Delegates and Lambdas
5/28/2009Edited 8/1/2010
While studying ASP.NET MVC Framework and Linq, I was running into unfamiliar C# syntax, delegates, anonymous methods, lambdas, Funcs, so I needed to step back to the beginning. In this article, which I wrote to help me understand it all, I begin with delegates and how they were coded in C# 1.0. Then I present a fully functioning console app that introduces lambdas that you can copy and paste into Visual Studio so you can experiment. The app presents other ways that C# has given programmers to express the notion of a pointer to a function or put another way, the notion of passing functionality around. Delegates become very important in LINQ in the form of Funcs and lambda expressions. So even though some of the code here may seem academic in that you can't see yourself using delegates in these ways, the examples will give you a good base of understanding.
If you're looking for information on C# events, a closely related topic, I have another article on Events, some of which requires knowledge of delegates and anonymous methods and lambda syntax to really understand fully. But you can still follow the coding patterns there and get your custom events to work even if you don't understand what you're doing. Po'gammers always seem to be under the gun and only have time to implement, not understand. I guess that's what evenings and weekends are for? Humm, is it time to get on the Ruby/Rails train? Still, learning this stuff is fun.
Delegates in C# 1.0
In C# 1.0, a delegate is an object like any other that you have to declare and instantiate.
- Create a delegate type
- Create an instance of the delegate type and assign it a value
- Use the delegate object for some purpose: "Invoke the delegate"
Create a delegate type
A delegate type will define the signature of the method that it will refer to and is type-safe as a result. So before you create your delegate type, you need to know what method it will refer to. For this example, I'll use this:
private static bool IsEven(int number)
{
return (number % 2 == 0);
}
As you can see, IsEven recieves an int and returns a bool. Now I can declare the delegate type. It's signature must match the signature of IsEven:
public delegate bool NumberPredicate( int number );
Using the delegate keyword, I declared a delegate type and named it NumberPredicate. It receives and int and returns a bool. It's like any other reference type you'd create. It's exactly as if I'd done this:
public class NumberPredicate()
{
bool Invoke(int number)
}
internal class NumberPredicate : System.MulticastDelegate
{
public NumberPredicate(Object obj, IntPtr method);
public virtual void Invoke(Int32 number);
public virtual IAsyncResult BeginInvoke(Int32 val, AsyncCallback callback, Object obj);
public virtual void EndInvoke(IAsyncResult result);
}
Create a delegate instance
So now I've got my delegate type and it's ready to be instantiated and assigned a value so I can use it. That's easy, just like any other object creation I'd use the new keyword to create an instance:
NumberPredicate evenPredicate = new NumberPredicate(IsEven);
But you're likely to see this instead...
NumberPredicate evenPredicate = IsEven;
...thanks to another C# compiler shortcut, which is called delegate inference. It's actually a C# 2.0 thing, so I'm jumping ahead a bit. Note that this shortcut might not ALWAYS work; the compiler can't read your mind. Did you catch another compiler trick in the first instantiation? That constructor call only has one argument and the constructor I showed above in the class that the compiler generates shows two arguments, Object and IntPtr. More hanky panky that makes your code more readable or to use the current buzzword, expressive.
My instance name is evenPredicate and the method it refers to is IsEven. But the constructor generated by the compiler that I showed above doesn't match the way I just called it! Again, the compiler is at work and confusing the crap out of me. It knows it's dealing with a delegate instantiation and translates the single parameter of the constructor call, IsEven, into the Object and IntPtr params that are required. If IsEven had been static, the Object param would have been null. The two arguments are stored in fields found in the MulticastDelegate class: _target and _methodPtr.
Use the delegate instance (Invoke it)
Now is where that Invoke method that the compiler generated comes in. To use the delegate, called invoking it, I do this:
int myInt = 8;
bool isItEven = evenPredicate.Invoke(myInt);
But you probably wont see this syntax because it ignores yet another C# shortcut, which is to just leave off the .Invoke. Here's the shortcut way of invoking a delegate:
bool isItEven = evenPredicate(myInt);
Now, using the shortcut syntax, the delgate invocation looks exactly like how you'd call your IsEven method.
bool isItEven = IsEven(myInt);
The shortcut syntax clarifies what the code is actually doing, though, like I said, hides how delegates are implemented under the covers. Seeing the Invoke in both the declaration and instatiation of the delegate type demystified delegates for me because I could see how they fit into syntax that's familiar. I like compiler shortcuts but there's something in me that just needs to see how they actually work. Now that I know, the shortcut syntax is much easier to read; more fluid, as they say.
I hope that all made sense. Now on to C# 2 and 3; anonymous methods, lambda expressions and Funcs.
A quick explanation about the terminology around lambda expressions is in order as a warning for those like me trying to get a good feel for them. Note that there are two very different kinds of lambda expressions, a delegate type and an expression tree type; this article discusses the delegate type only. Don't get confused when you run across lambda expression tree types. They don't get executed but are turned into a data structure by the compiler that gets inspected at runtime by some framework or other, like LINQ to SQL in the .NET Framework. I'll use the term lambda and lambda method and lambda expression but for this article they're all delegates (or anonymous methods). I see various kinds of terminology used so I'm not sure which is technically correct. Based on MSDN it looks like it's all lambda expressions and you just use the context to distinguish between a delegate type lambda and an expression tree type.
Compiler Progression of Delegates
I identified four steps in the progression of C# delegate syntax :- C# 1 - Delegate that points to a declared method, what we just did above. (See my blurb on Callbacks for an example of one way you might use a delegate.)
- C# 2 - Delegate that points to an anonymous method, a method without a name.
- C# 3 - Delegate pointing to a Lambda method, another syntax to express an anonymous method.
- C# 3 - Delegate replaced by a Func.
Below is a console app to demonstrate each of these steps. I put a lot of comments in the code so you'll have them in your solution if you copy and paste this stuff into a Visual Studio console app to experiment with it. Doing a trace to see the callback work is interesting. In the code comments, if I use the word delegate alone, you'll have to infer from the context what I really mean: delegate type or delegate instance. I start out being explicit but I might get lazy later on.
The app ends with a quick example of using a Lambda method as a call parameter and a Func in a method signature, a reuseability technique. This type of method signature, where you have a Func as a parameter, is very common in the linq world of extension methods. More on that topic in this article on Extension Methods and Linq.
using System;
// This example borrows from various sources including MSDN and C# 2008
// for Programmers Third Edition, from Deitel & Deitel, the book I seem
// to be relying on most for consistently good explainations.
// I stole the IsEven callback predicate from Dietel because I liked it
// for it's simplicity, (a callback predicate is defined as a method that
// returns a bool and accepts 1 parameter).
namespace Lambda
{
class Delegates
{
// This program shows a progression of the ways you can create and use
// delegates in C#, from C# 1 to 3. It turns out
// that passing around code statements as parameters is useful.
// C# 1. Introducing Delegates: Pointers to methods
// C# 2. Anonymous methods
// C# 3. Lambda methods
// C# 3. Func<T, TResult>
// Here, we declare a delegate type we'll name "NumberPredicate".
// It tells the compiler the method signature that delegates of this delegate
// type will represent. In this case, it's a method that returns a bool and
// receives an int. In Main, we'll instantiate the delegate type
// and assign it to a method. In this example, we assign it to
// the IsEven method - (IsEven is down at the bottom of this code listing).
public delegate bool NumberPredicate( int number );
// StringLengthCompare delegate used to illustrate a point about Lambda methods
public delegate bool StringLengthCompare(int number, string s);
static void Main( string[] args )
{
// C# 1.0 Delegates - pointers to methods
// We have a declared method, IsEven(), (scroll down near the bottom of this listing).
// We've declared a delegate type, (see above, just before Main).
// We instantiate the delegate type and assign it to the address
// of the IsEven method. Next line creates a delegate instance.
NumberPredicate evenPredicate1 = IsEven;
// Now we use the delegate instance: it's just like calling the IsEven method: IsEven(4).
Console.WriteLine("Call the IsEven() declared method using a delegate variable: {0}",
evenPredicate1(4));
// C# 2.0 Anonymous method assigned to a delegate instance
// I've seen this called an anonymous delegate.
// You can eliminate the need for a full blown method delaration by using an
// anonymous method in the delegate assignment statement. You put the
// method's code inline, directly inside the delegate declaration.
// So in C# 2, we're no longer using the named IsEven method that appears
// at the end of the listing.
// We still have a declared delegate type, NumberPredicate.
// When you make the IsEven method anonymous, you replace the method name, IsEven,
// with the delegate keyword. Otherwise, the method looks the same
// as the named version.
// Here, the return type, bool, is implied by the method itself and the compiler
// is smart enough to figure out that "return(number % 2 == 0)" means the
// return type is a bool.
NumberPredicate evenPredicate2 = delegate(int number) { return(number % 2 == 0); };
// Or, to use a carriage return or two to make things more clear:
NumberPredicate evenPredicate22 = delegate(int number)
{
return (number % 2 == 0);
};
Console.WriteLine("The IsEven method no longer exists: it's anonymous: {0}",
evenPredicate2(4));
// C# 3.0 Lamba Methods
// A lambda isn't that scary - it's just another syntax for showing an anonymous
// method inline, a more terse syntax that eliminates the return keyword.
// => is the lambda operator.
// To the right of the lambda operator is the statement or expression.
// To the left of the operator are the input parameters
// The => operators is read as, "goes to", if that helps. (Not really.)
// The next line shows a lambda with multiple input params:
StringLengthCompare stringLengthCompare = (int x, string s) => s.Length > x;
bool isLonger = stringLengthCompare(3, "mystring");
Console.WriteLine("Is the string longer? {0}", stringLengthCompare(3, "mystring"));
// With a lambda, you can also eliminate the input param types as well as the return
// keyword: The compiler figures it all out.
StringLengthCompare stringLengthCompare2 = (x, s) => s.Length > x;
// Note that the StringLengthCompare delegate type was declared above, before Main().
// The StringLengthCompare lambda receives an int and a string and returns a bool,
// the result of the comparion of the string's length to the int that was passed in.
// (Example stolen from MSDN: http://msdn.microsoft.com/en-us/library/bb397687.aspx.)
// So, to recap, a lamba is another syntax for an anonymous method.
// With a lambda, you no longer need the delegate keyword,
// the return keyword, the input param types, the curly braces. It's terse syntax
// and takes a lttle getting used to.
// Back to our historical tour and some review and clarification....
// This next line shows a lambda method being assigned to our NumberPredicate delegate.
// The lambda eliminates the need for the delegate keyword and optionally,
// the curly braces, but only if your lambda is only one statement.
// With Lambdas, the param can be explicitly typed or not. (Though in some cases
// the compiler may not be able to determine the type, in which case
// you need to type it explicitly.)
// implicitly typed paramter
NumberPredicate evenPredicate3 = number => (number % 2 == 0);
// explicitly typed parameter
NumberPredicate evenPredicate4 = (int number) => (number % 2 == 0);
// If you keep the curly braces, the lambda method is called a lambda statement.
// If your anonymous method has more than one statement, you need the curlies.
// implicitly typed paramter
NumberPredicate evenPredicate5 = number => { return (number % 2 == 0); };
// Explicit param type
NumberPredicate evenPredicate6 = (int number) => { return (number % 2 == 0); };
// Two statements need curlies....
NumberPredicate evenPredicateNOT = number => { number += 1; return (number % 2 == 0); };
Console.WriteLine("A delegate variable, assigned to a Lambda method: {0}",
evenPredicate3(4));
Console.WriteLine("This is a NOT IsEven call: {0}",
evenPredicateNOT(4));
// C# 3.0 Func.
// With a Func, you don't need a delegate type; the Func IS the delegate.
// Funcs can be used with named methods and anonymous methods and lambda methods.
//
// There are many overrides, each one allowing for more input params:
// Func<TResult> - a method signature with no params and return type of TResult.
// Func<T, TResult> - one param and return type of TResult.
// Func<T1,T2, TResult> - two params. and so on to 4 params in .Net 3.5 and
// 16 params in .Net 4.0
// Here are three ways you can assign a Func delegate...
// Declare a Func and assign it to a named method.
Func<int, bool> funcyThing1 = IsEven;
// Declare a Func and assign it to an anonymous method.
Func<int, bool> funcyThing2 = delegate(int number) { return (number % 2 == 0); };
// Declare a Func and assign it to a Lambda.
Func<int, bool> funcyThing3 = number => (number % 2 == 0);
Console.WriteLine("Using a Func: {0}",
funcyThing3(4));
// Example of passing a Lambda method as a parameter in a method call.
// (Scroll down to the SumIt method below.)
// Do a debug trace to see this in action.
// Here I want to find the sum of 10 + 20 + 30 + 40.
// SumIt is a named, declared method that returns an int and recieves 2 ints
// and a delegate. See the comments for SumIt for more info.
// x => x * 10 is a lambda method that receives an int param and
// the compiler figures out that it returns an int, based on the fact that
// and int times 10 results in an int.
int mySum = SumIt(1, 5, x => x * 10);
Console.WriteLine("Result of my method call with a Lambda param: {0}",
mySum);
Console.ReadKey();
} // end main
// This is the SumIt method used to show how a Lambda method is passed as a
// parameter to another method.
// Each time the for loop runs, the delegate, multipyByTen, is called, passing
// an int into the anonymous function.
// I gave the Func a fairly general name, process, instead of something more
// specific, like multiplyByTen, to illustrate that this type of callback
// design will support any type of operation that the caller sends as a param.
// The caller could have sent in something like: x => x * x, to give
// 1 + 4 + 9 + 16 + 25.
public static int SumIt(int start, int stop, Func<int, int> process)
{
int z = 0;
for (int i = start; i < stop; i++)
{
z = z + process(i);
}
return z;
}
// The IsEven named method declaration. You don't need a named, declared method
// if you make it anonymous and use it inline in a delegate assignment.
private static bool IsEven(int number)
{
return (number % 2 == 0);
}
} // End class Delegates
} // End Lambda namespace
A note: There's something embedded in this example that may be usefull for you to know someday, if you don't already. I use a predicate method, which is a method that returns a bool and receives one parameter. I used it because it was simple, but predicates are otherwise important, as they're used as paramers to certain generic collection methods. Here's a good example on MSDN: http://msdn.microsoft.com/en-us/library/bfcke1bz.aspx. Predicates are also used in Linq queries in Lambda form where the lambda operator, =>, is often read as "goes to".
A final note: C# 3.0 does a lot that I'm not used to. Anonymous types and methods, the var keyword, LINQ. I'm having some trouble with a complier that figures out so much without being explicitly told. I thought I could ignore some of it and save it for later, but quickly found out that's impossible if I want to learn about the ASP.NET MVC Framework. The Framework itself relies on the newer compiler capabilities and Linq is found in many ASP.Net MVC examples and books. You might get by without Linq but there's so much of it out there in MVC samples and you can only look at so much code that you don't understand before your learning effort suffers. So add these features to your learn list. And another topic that's kind of hot now, as I edit this article on 8-1-2010, with the popularity of functional languages and C#'s functional features, is closures. As far as I know, a closure in C# refers to the ability of an anonymous method to access variables outside of it's scope but within the scope of the method where the anonymous method exists. Such a varible used by an anonymous method is called a captured variable and they come in handy when you need to provide data to an anonymous method beside anything you pass in as params; something which might have required an additional class. There's good coverage in Jon Skeet's book, C# in Depth and you might look into closures in the JavaScript world.