Wednesday, April 02, 2008

Beginning Lambda Expressions in C#







I think I've been using LINQ to SQL for about a week now, and one of the (many) things to conquer on that particular learning curve are lambda expressions.

Lambda expressions are a new feature of C# 3.0 using .Net 3.5, and look something like this:
t => t.Contains("hello")
When starting out this expression can look very strange, but once you get your head around it they are pretty simple... well, in most cases.

Lambda expressions are like anonymous delegates, they have input parameters, and an output result, and the new => syntax ties them both together. What the example above means is that there is a parameter "t" (in this case a string), and it returns a bool (the return type of the string.Contains method).

But how do we know what these types are? They can be inferred from the type declaration. The above example would be meaningless on it's own, but when put together like this:
// delegate that takes a string and returns a bool
public delegate bool CheckString(string arg);

// create an instance of the delegate using a lambda expression
CheckString newDelegate = s => s.Contains("hello");

// use the delegate to show the expression works
Console.WriteLine("Value: {0}", newDelegate("hello world"));
We can see that the types are inferred from the fact we are creating a CheckString delegate. The delegate would return true when called with this string.

So how does this help us with LINQ? Well, in LINQ queries you'll often see that you need to specify parameters that have types that contain something like this:
Func<T0, TR>
And what the hell does that mean? Well, as part of the framework we have the following generic delegates already defined for us:
public delegate TR Func<TR>();
public delegate TR Func<T0, TR>(T0 a0);
public delegate TR Func<T0, T1, TR>(T0 a0, T1 a1);
public delegate TR Func<T0, T1, T2, TR>(T0 a0, T1 a1, T2 a2);
public delegate TR Func<T0, T1, T2, T3, TR>(T0 a0, T1 a1, T2 a2, T3 a3);
These delegates simply say that given the types specified, the delegate should return a type. In an abstract way, that's all a delegate is really: so don't worry that it can look a bit crazy.

Using this I could rewrite the previous example like this:
// create a lambda expression
Func<string, bool> newDelegate = s => s.Contains("hello");

// use the delegate to show the expression works
Console.WriteLine("Value: {0}", newDelegate("hello Mr Coupland"));
I don't have to define a CheckString delegate any more, and can just use the inbuilt generics to specify what the delegate interface is.

So what does this all mean? Well, if you need to pass in delegates to a method call, instead of having to define the delegate's interface, then structuring an anonymous delegate to match it, you can just use one of the Func delegates and a lambda expression, like in this really contrived example:
// create a boring method that doesn't do much
public bool StringConditionCheck(string str, Func<string, bool> exp)
{
return exp(str);
}

// call the method from somewhere else
public void MyOtherMethod()
{
Console.WriteLine(StringConditionCheck("sjdl", s => s.StartsWith("s")));
}
That example's probably a little too simple, but it shows what you can do with a lambda expression passed in to another method.

This is nothing new in itself, you can do all of that with anonymous delegates in .Net 2.0 (and with normal delegates before that), but now there's less code to write.

And when you can create delegates so effortlessly, it makes creating generic Expressions for our LINQ expression trees really simple, but that's for another post.

5 comments:

Anonymous said...

Hello Ian,

I like the delegate:

public delegate TR Func< TR >();

But how would I instantiate a delegate which returns void?

For example,

public void MyFunction()
{

}

// in Main():

Func< void > f = MyFunction;

This does not work but is there a built in way to do this?

Ian Dykes said...

Well from the point of view of a lambda expression there would be little point in a delegate that returned void, as one of the features is really to translate one (or more) parameters into another variable, be it of the same type or not. So really a delegate that returns void and takes no parameters wouldn't really fit into the lambda pattern.

So that's why there's no predefined delegate for this type, you would have to create your own. Something like:

public delegate void MyDelegate();

Then use it as you would a normal delegate.

Anonymous said...

Actually that makes sense because something that can be fit into a lambda expression must return something.

Of course if you wanted to have a family of delegates:

public delegate void VoidFunc< T > ()
public delegate void VoidFunc< T1, T2 > ()

etc, then we are free to do so.

Akidan said...

anonymous, you want to take a look at the extended set of Action delegates built into 3.5. They work just like the Function delegates, but 'return' void.

All of these are in System.Core, except for the second, which is in mscorlib.

public delegate void Action();
public delegate void Action<T>(T obj); // This one's in 2.0 too.
public delegate void Action<T1, T2>(T1 arg1, T2 arg2);
public delegate void <T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3);
public delegate void Action<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);

Lambda expressions are indeed allowed to return void. For example:

Action<string> writer = s => Console.WriteLine(s);

is valid.

Anonymous said...

thanks akidan

it makes a lot of sense to call it an "Action" instead of "VoidFunc" as an action will definately have a side affect whereas a REAL function does not and so a real void function will just return void each time instead of having a side effect.