Thursday 5 July 2012

C# Anonymous, Lambdas, Delegates and Expressions...phew.

C# and by extension any .net language has complex support for both anonymous functions and lambdas. I found it quite useful to have a basic reference for these; so here it is.
// Anonymous function using the delegate keyword.
Func<string> anon = delegate() { return "x"; });

// Anonymous function from lambda.
Func<string> lambda = () => "x";

// Same but using explicit typing.
var anon2 = new Func<string>(delegate() { return "x"; });
The function references (anon, lambda, anon2) are known in .net as delegates which is nomenclature for function pointer. Because C# is statically typed we have to explicitly type the delegate, hence 'Func'. Func is a generic delegate type: a predefined delegate, rather than having to specify it constantly. We could explicity define the delegate like this.
// Define delegate.
delegate int myDelegate(int i);

// Assign delegate.
myDelegate anon = delegate(int i) { return i };
myDelegate lambda = x => x + 1;

Linq Expressions is where things start to get more complex. Linq expressions allow lambda functions to be reflected and turned into data instead of code, allowing you to change the function. A typical change would be from a .net lambda function to an SQL statement.
// Expression
Expression<Func<string>> exp = () => "x";

// Can't be done.
// Expression<Func<string>> exp2 = delegate() { return "x"; };
However note that Linq Expressions cannot be created using anonymous functions.

OK if you followed all that....lambda can also be written with a statement body rather than an expression body as we have seen so far. Statement bodies allow more complex multiline functions.
// Statement lambda
Func<string> lambda = () => { return "x"; };
Statement and expression body lambdas are functionaly identical, but they are different. Statement body lambdas cannot be made into expression trees. In practice this means that certain linq providers, such as Linq2Sql cannot use lambda functions created with statement bodies.
A lambda expression with a statement body cannot be converted to an expression tree
One final quirk I'll mention is that once you have an explicit Expression it can be used on an IQueryable, but not a standard IEnumerable. You have to 'unwrap' it, converting it back to a standard Func.
// OK, compiler sorts it out
var l0 = new int[] { 1, 2, 3 }.Where(x => x < 2);
var l1 = new int[] { 1, 2, 3 }.AsQueryable<int>().Where(x => x < 2);

// Force exp to be an Expression
Expression<Func<int, bool>> exp = x => x < 2;

// Doesn't work - invalid arguments
//var l2 = new int[] { 1, 2, 3 }.Where(exp);

// Works on IQueryable
var l3 = new int[] { 1, 2, 3 }.AsQueryable<int>().Where(exp);

// Convert Expression back to standard Func to make it work.
var l4 = new int[] { 1, 2, 3 }.Where(exp.Compile());