We now have an idea that anonymous methods can help us create a simple and short method. However, in C# 3.0, lambda expressions were announced in order to complement anonymous methods in providing a shorthand notation to create anonymous methods. In fact, lambda expressions become the preferred way when writing new code.
Now, let's examine the simplest lambda expression syntax, as follows:
([parameters]) => expression;
In the lambda expression syntax, we only find two elements, which are parameters
and expression
. Like any method, a lambda expression has an argument symbolized by parameters. The implementation of the lambda expression is symbolized by the expression. We can also omit the parenthesis of parameters if only one parameter is required.
Let's create a simple lambda expression, which we can find in the SimpleLambdaExpression.csproj
project, as follows:
public partial class Program { static Func<string, string> displayMessageDelegate = str => String.Format(Message: {0}", str); }
In the preceding code, we declare the displayMessageDelegate
delegate and assign it to the Func
delegate using a lambda expression. Similar to the method in the SimpleDelegates.csproj
project, in order to invoke the delegate, we use the following code:
public partial class Program { static void Main(string[] args) { Console.WriteLine( displayMessageDelegate( "A simple lambda expression sample.")); } }
We call the displayMessageDelegate
delegate like a method name. The output will be sent to the console, as follows:
Now, let's compare the method declaration between an anonymous method in SimpleAnonymousMethods.csproj
and a lambda expression in the SimpleLambdaExpression.csproj
project:
static Func<string, string> displayMessageDelegate = delegate (string str) { return String.Format("Message: {0}", str); };
The preceding code snippet is an anonymous method declaration that is shorter and simpler than a named method declaration.
static Func<string, string> displayMessageDelegate = str => String.Format("Message: {0}", str);
The preceding code snippet is a lambda expression declaration that is shorter and simpler than an anonymous method. The lambda expressions are brief compared to anonymous methods.
Now, let's discuss the transformation of an anonymous method to a lambda expression. We have the following anonymous method:
delegate (string str) { return String.Format("Message: {0}", str); };And we want to transform it to a lambda expression, as follows:
str => String.Format("Message: {0}", str);
First, we take out the delegate
keyword since we don't need it anymore; so, the code will be as follows:
(string str) { return String.Format("Message: {0}", str); };
Then, we supersede the curly braces with a =>
lambda operator in order to make it the inline lambda expression:
(string str) => return String.Format("Message: {0}", str);
We can also remove the return
keyword since it is only a single line code that returns a value. The code will be as follows:
(string str) => String.Format("Message: {0}", str);
Since the preceding syntax is now an expression instead of a complete statement, the semicolon can be removed from the preceding code and the code will be as follows:
(string str) => String.Format("Message: {0}", str);
The preceding expression is a valid lambda expression. However, we can simplify the code more in order to take advantage of the lambda expression. The code will be as follows:
(str) => String.Format("Message: {0}", str);
Since we have taken out string
data type, we can now take out the parenthesis as well:
str => String.Format("Message: {0}", str);
The preceding syntax is our final lambda expression. As we can see, now, our code becomes more readable because of its simplicity.
Using lambda expressions, we can actually create delegates and expression tree types in anonymous methods. Now, let's find out the difference between these two types in the upcoming topics.
We discussed lambda expressions in a delegate type when we created code in the SimpleLambdaExpression.csproj
project. Now, let's create another project name in order to discuss this by referring to the following code:
public partial class Program { private static Func<int, int, int> AreaRectangleDelegate = (a, b) => a * b; private static Func<int, int, int> AreaSquareDelegate = (x, y) => x * y; }
Again, we refactor the SimpleDelegatesRefactor.csproj
project and replace the anonymous method with a lambda expression. As we can see, the lambda expression is assigned to a variable typed delegate. Here, we create a lambda expression in a delegate type. We can use the Main()
method we had used in the SimpleDelegatesRefactor.csproj
project to invoke AreaRectangleDelegate
and AreaSquareDelegate
. The result of the two projects will be completely the same.
Besides creating a delegate, we can create expression trees, which are data structures which represents the expression elements (expr, term, factor) as a tree. By traversing the tree, one can interpret the expression trees or we can mutate a node in the tree for transforming the code. In compiler parlance, expressions trees are called abstract syntax trees (AST).
Now, let's take a look at the following code snippet in order to assign a lambda expression to the delegate that we discussed earlier:
Func<int, int, int> AreaRectangleDelegate = (a, b) => a * b;
As we can see, there are three sections in the preceding statement. They are as follows:
Func<int, int, int> AreaRectangleDelegate
=
(a, b) => a * b
We are going to translate the preceding code statement into data. To achieve this goal, we need to create an instance of the Expression<T>
type, and T
is delegate type. The Expression<T>
type is defined in the System.Linq.Expressions
namespace. After using this namespace in the project, we can translate our preceding code into an expression tree, as follows:
public partial class Program { static void Main(string[] args) { Expression<Func<int, int, int>> expression = (a, b) => a * b; } }
We have converted our preceding delegate lambda expression into the expression tree declared to be of type Expression<T>
. The variable expression in the preceding code is not executable code but a data structure called an expression tree. There are four essentials properties in the Expression<T>
class that we will discuss in detail. They are as follows:
ExpressionType
type of node in the treeNow, let's add a breakpoint in the expression variable and run the debugging process by pressing F5 in the LambdaExpressionInExpressionTree.csproj
project. After executing the expression declaration line, we can take a peek at the variable window in the Visual Studio IDE, and we will get the following screenshot:
From the preceding screenshot, we have a Body
property containing {(a * b)}
, NodeType
containing Lambda, Type
containing the Func
delegate with three templates, and are two parameters. If we expand the Body
information in the variable window, we will get a result similar to what is shown in the following screenshot:
From the preceding screenshot, we can see that we have the Left
property containing {a}
and the Right
property containing {b}
. Using these properties, we can also explore the body of expression tree programmatically. The following code is the exploreBody()
method, which will explore the properties of Body
:
public partial class Program { private static void exploreBody( Expression<Func<int, int, int>> expr) { BinaryExpression body = (BinaryExpression)expr.Body; ParameterExpression left = (ParameterExpression)body.Left; ParameterExpression right = (ParameterExpression)body.Right; Console.WriteLine(expr.Body); Console.WriteLine( " The left part of the expression: {0} " + " The NodeType: {1} " + " The right part: {2} " + " The Type: {3} ", left.Name, body.NodeType, right.Name, body.Type); } }
If we run the preceding exploreBody()
method, we will get the following output:
In the preceding code, we access the Body properties of Expression<T>
programmatically. We need to create a BinaryExpression
data type in order to get the Body
content, and ParameterExpression
in order to get the Left
and Right
properties content. The code snippet for the BinaryExpression
and ParameterExpression
data is as follows:
BinaryExpression body = (BinaryExpression)expr.Body; ParameterExpression left = (ParameterExpression)body.Left; ParameterExpression right = (ParameterExpression)body.Right;
We have successfully created a data structure from the code in the expression tree. We can, if we want, convert this data back into code by compiling the expression. The expression we have is as follows:
Expression<Func<int, int, int>> expression = (a, b) => a * b;
So, we can compile the expression and run the code in the expression using the following compilingExpr()
method:
public partial class Program { private static void compilingExpr( Expression<Func<int, int, int>> expr) { int a = 2; int b = 3; int compResult = expr.Compile()(a, b); Console.WriteLine( "The result of expression {0}"+ " with a = {1} and b = {2} is {3}", expr.Body, a, b, compResult); } }
If we run the compilingExpr()
method, the following output will be displayed on the console window:
As we can see, we have compiled the expression using the Compile()
method in the expression class, as follows:
int compResult = expr.Compile()(a, b);
The expr.Compile()
method produces a delegate of type Func<int, int, int>
in accordance with the type of the expression. We give the Compile()
method the arguments a
and b
based on its signature, then it returns the int
value.