Action & Func: Never write another delegate
With lambda expressions in C#, the Func
generic delegate and it's variations have been getting a lot of attention. So naturally, you might think that the lambda syntax is just a shortcut for creating anonymous delegates, whether they return values or not.
First let's look at the evolution of delegates from 1.1 to now. Delegates, simply are the method equivalent of function pointers. They let you pass a method call as an argument for later execution. The cool thing (and a garbage collection pitfall) is that a delegate creates a lexical closure, i.e. the delegate carries with it the object that the method gets called on. For garbage collection this means that a delegate prevents an object from being collection. That's why it's important to unsubscribe from those events you subscribed to.
But I digress. Let's define a delegate that returns an Integer and a method that matches that delegate:
With the original .NET 1.0 syntax we'd create the delegate like this:
Now we can call p1()
and get an integer back, and since it's closure, each time we call p1() the originating objects x increases as does our return value.
Then, in .Net 2.0 we got anonymous delegates.
IntProducerDelegate p2 = delegate { return IntProducer(); };
// or with IntProducer's action inlined...
IntProducerDelegate p3 = delegate { return x++; };
This got rid of the need to create a method just to pass along a closure that manipulated our object at a later time. The other thing that anonymous delegates re-inforce is that delegates just care about signature. IntProducerDelegate can get assigned any delegate that takes no argument and returns an int. That sounds like a perfect scenario for generics and in .NET 3.5, we got just that, a set of generic delegates called Func. Using Func, we quickly get to our lambda expression replacing the original delegate syntax like this:
// create a new Func delegate just like the IntProducerDelegate
IntProducerDelegate p3 = new Func<int>(IntProducer);
// which means that we don't need IntProducerDelegate at all anymore
Func<int> p4 = delegate { return x++; };
// and the anonymous delegate can also be shorthanded with a lambda expression
Func<int> p5 = () => { return x++; };
// which says, given that we take no argument "()", execute and return the following "return x++;"
However, before there ever was Func, .Net 2.0 introduced the generic delegate Action, which is a natural counterpart to Func, encapsulating a method that does not return anything. Following through the example of the producer, we'll create a consumer like:
delegate void IntConsumerDelegate(int i);
public void IntConsumer(int i)
{
Console.WriteLine("The number is {0}", i);
}
Now following the same evolution of syntax we get this:
IntConsumerDelegate c1 = new IntConsumerDelegate(IntConsumer);
IntConsumerDelegate c2 = new Action<int>(IntConsumer);
Action<int> c3 = delegate(int i) { Console.WriteLine("The number is {0}", i); };
Action<int> c4 = (i) => { Console.WriteLine("The number is {0}", i); };
So lambda syntax can be used to create either a Func or an Action. And that also means that we never have to explicitly need to create another delegate, being able to use a variation of these two generic delegates as our arsenal for storing lambda expressions of all kinds.