Lambda expressions are a great way to write simple anonymous delegates in a concise way. Of course you aren’t limited to simple functions, you can write a full blown method in lambda syntax.
I’ve already shown some lambda expressions in use when I discussed extension methods. Here’s the example:
1
|
|
There are two lambda expressions in the above example. They are:
item => item.Price < 1
item => item.Name
These are very simple lambda expressions that take one parameter (item
) and
return a result. The type of the parameter and the the type of the result are
inferred by the compiler allowing us to express what clearly without
having to decorate it with types. So each of the expressions really means the
following:
- Take an item and return whether the item’s price is less than one.
- Take and item and return the item’s name.
Hopefully you can see the basic pattern here. Take what’s on the left of the
lambda operator (=>
), use it in the expression on the right and return the
result of the expression.
The important thing to remember with lambdas is that they only declare the
function. In the example above the lambda expression is executed within the
Where
and Select
methods and is executed once for each item in the
enumeration. The Where
method uses the result of the lambda expression to
determine if the item should be in the resultant enumeration and the Select
method returns the result on the lambda expression as the member of the
enumeration.
Invoke()
made easy
Lambdas aren’t restricted to being used just with LINQ, they can be used anywhere that anonymous delegates can be found. One area I’ve found lambdas increasingly useful is in multi-threaded applications. For example, my Tweet demo uses multiple threads to perform the animation. Consequently I often needed to update the UI from the background thread. Because this isn’t directly allowed I needed to send the code to the UI thread. Before anonymous delegates I would need to create a full blown method to perform a single task. That’s a lot of extra work for something that is unlikely to be re-used elsewhere. With anonymous delegates I can define the method inline, which is great, but still uses a lot of extra decoration. Now with lambdas I can finally get to the work of just having my code. Here’s an example straight from that demo.
1 2 3 4 5 6 7 |
|
Perhaps the most interesting part of the code is the use of the title
variable within the lambda expression. In this instance, title
is a local
variable within the method that is calling BeginInvoke()
. The anonymous
delegate will use this local reference when it is called. You can’t always get
away with this. Fortunately strings are immutable in .NET,
so we can be confident that the value will not change. If title was mutable
(can be changed) its value could be modified after BeginInvoke()
is called,
but before it is used in the lambda expression. This may lead to unexpected
results.
This problem isn’t just isolated to multi-threaded applications (although multi-threaded applications are inherently more unpredictable). Because LINQ queries are not executed until they are enumerated (LINQ and Deferred Execution) they are susceptible to the same problems, but fortunately in a more consistent way. So remember to always be wary when using a local variable in a LINQ query.
Generic Delegates in .NET 3.5
Version 3.5 of the .NET Framework introduced some new generic delegates designed to cover most cases. In fact, it is unlikely that you will need to define your own delegates unless you need more than four parameters.
The Action
delegates
Action
delegates refer to a method that does not return a value (a void
method).
Action
is non-generic delegate that takes no parameters and does not return a value.Action<T>
was originally introduced in .NET 2.0. This delegate takes one parameter of typeT
.Action<T1, T2>
,Action<T1, T2, T3>
andAction<T1, T2, T3, T4>
are generic delegates that take two, three and four parameters respectively and do not return a value.
The Func
delegates
Func
delegates are similar to the Action delegates except that they also
return a value. The type of the value is always the last type parameter of the
generic delegate.
Func<TResult>
is a generic delegate that takes no parameters and returns a value of typeTResult
.Func<T, TResult>
,Func<T1, T2, TResult>
,Func<T1, T2, T3, TResult>
andFunc<T1, T2, T3, T4, TResult>
are generic delegates that take one, two, three and four parameters respectively and return a value of typeTResult
.
What’s next?
Next up we’ll be looking at LINQ to SQL and how it can make accessing and using a database a joy.