12.5. Lambda Expressions

Over successive versions of the .NET Framework, the syntax with which you can define and reference reusable functions has evolved. In the early versions, you had to explicitly declare a delegate and then create an instance of it in order to obtain a reference to a function. In version 2.0 of the .NET Framework, C# shipped with a new feature called anonymous methods, whereby you could declare a multiple-line reusable function within a method. Lambda expressions in their simplest form are just a reduced notation for anonymous methods. However, lambda expressions can also be specified as expression trees. This means that you can combine, manipulate, and extend them dynamically before invoking them.

To begin with, let's examine the following simple lambda function, which takes an input parameter, x, increments it, and returns the new value. When the function is executed with an input value of 5, the return value assigned to the variable y will be 6.

VB.NET

Dim fn As Func(Of Integer, Integer) = Function(x) x + 1
Dim y As Integer = fn(5)

C#

Func<int, int> fn = x => x + 1;
int y = fn(5);

You can see that in both languages the type of fn has been explicitly declared. Func is a generic delegate that is defined within the framework and has five overloads with a varying number of input parameters and a single return value. In this case there is a single input parameter, but because both input and return values are generic, there are two generic parameters. From the earlier discussion of implicit typing and anonymous types, you would expect that you could simplify this syntax by removing the explicit reference to the Func delegate. Unfortunately, this is only the case in VB.NET, and you will notice that we need to specify what type the input variable x is.

VB.NET

Dim fn = Function(x As Integer) x + 1
Dim y As Integer = fn(5)

Manipulating the input parameters has only limited benefits over the more traditional approach of creating a delegate to a method and then calling it. The lambda syntax also enables you to reference variables in the containing method. To do this without lambda expressions would require a lot of code to encapsulate the referenced variables. Take the following snippet, wherein the method-level variable inc determines how much to increment x by. The output values y and z will end up with the values 20 and 25 respectively.

Dim inc As Integer = 10
Dim fn = Function(x As Integer) x + inc
Dim y = fn(10)
inc = 15
Dim z = fn(10)

Although VB.NET has better support for implicit typing with lambdas, C# has a much richer functionality when it comes to what you can do within a lambda expression. So far we have only seen a lambda expression with a single expression that returns a value. An alternative syntax for this uses curly braces to indicate the beginning and end of the lambda expression. This means that C# can have multiple statements, whereas VB.NET is limited to only a single return expression.

int inc = 5;
Func<int, int> fn = x => {inc += 5;
                          return x + inc; };

Using this syntax (that is, using the curly braces to delimit the lambda body) means that the expression cannot be referenced as an expression tree.

It is also possible to create lambda expressions with no input parameters, and, with C#, lambda expressions with no return values. Because C# doesn't support implicit typing with lambda expressions, you have to either use one of the existing zero-argument, no-return values or delegates, or create your own. In the following snippet you can see that fn is a MethodInvoker delegate, but we could have also used the Threadstart delegate, as it has the same signature.

VB.NET

Dim inc as Integer = 5
Dim noInput = Function() inc + 1

C#

int inc = 5;
Func<int> noInput = () => inc + 1;
System.Windows.Forms.MethodInvoker noReturn = () => inc += 5;

All these scenarios are lambda expressions, but you may come across references to lambda functions and lambda statements. Essentially, these terms refer to specific subsets of lambda expressions, those that return values and those that don't, respectively.

Another aspect of lambda expressions is that they can be represented as expression trees. An expression tree is a representation of a single-line lambda expression that can be manipulated at runtime, compiled (during code execution), and then executed.

VB.NET

Imports System.Linq.Expressions
...
Dim fnexp As Expression(Of Func(Of Integer, Integer)) = Function(x) x + 1
Dim fnexp2 = Expression.Lambda(Of Func(Of Integer, Integer))( _
                             Expression.Add(fnexp.Body, Expression.Constant(5)), _
                             fnexp.Parameters)
Dim fn2 = fnexp2.Compile()
Dim result = fn2(5)

C#

using System.Linq.Expressions
...
Expression<Func<int, int>> fnexp = x => x + 1;
var fnexp2=Expression.Lambda<Func<int,int>>(
                         Expression.Add(fnexp.Body, Expression.Constant(5)),
                         fnexp.Parameters);
Func<int, int> fn2 = fnexp2.Compile();
int result = fn2(5);

As you can see from this example, we have taken a simple lambda expression, represented as an expression tree (by means of the Expression class), and then added a constant of 5 to it. The net effect is that we now have another expression tree that takes x, adds 1 to it, then adds 5 to it, giving a result of 11. As each operation typically has two operands, you end up with an in-memory binary tree of which the leaves represent the operands, which are linked by operators.

Expression trees are an important concept when you consider that Linq was intended to be a query language independent of implementation technology. When a lambda expression is represented as a tree (and you will see later that LINQ statements can also be represented as expression trees), the work of evaluating the expression can be passed across language, technology, or even machine boundaries. For example, if you have an expression that selects rows from a database, this would be much better performed in T-SQL within the SQL Server engine, perhaps even on a remote database server, than in .NET, in memory on the local machine.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.186.83