Friday, August 07, 2020 00:36

Table of contents >> Delegates, Lambda Expressions, Events > Lambda expressions

Lambda expressions

In the previous lesson I was writing that we can further improve our code by using lambda expressions. A lambda expression is a convenient way of defining an anonymous (unnamed) function that can be passed around as a variable or as a parameter to a method call. To start with, this was the code we ended up with in the previous lesson:

So, the three methods that I declared as candidates for the FilterDelegate delegate, LessThanFive, LessThanTen, GreaterThanThirteen, are very similar one to another. Whenever we have codes that look strikingly similar, we are doing something wrong. In fact what differs in our three methods? If you analyze the syntax, the only difference comes in this piece of code: _number < 5;. That’s all we really need from those methods, and that’s all that relevant and needs to be parameterized.

A lambda expression allows us to declare a method without a name that is used in a single place, and is declared locally. And if you look at our codes, we are using our three methods in a single place, as a parameter to the RunNumbersThroughFilter method. So, if a lambda expression is only declared locally, let’s do just that, let\s move one of our method’s declaration in the place where we actually need it:

At this point, you will get compiler errors, but disregard them, we will clear things up a bit. Because, if you think about it, the keyword static has no meaning in that local context, so, we can delete it:

Next, the keyword bool that tells us the return type of the method: well, I can look at this expression and see that it obviously returns a boolean: _number < 5. So, I don’t need the bool keyword either:

Now, the LessThanFive method, since we are converting it to a lambda expression, and lambda expressions are only declared and used locally, only exists in the piece of code where we declare it, directly inside the call to the RunNumbersThroughFilter function. We are not going to call it anywhere else. So, we don’t really need a name for it, either:

We see that the declaration states that _number is an int. Well, I KNOW it is an int, because FilterDelegate takes an int. We are passing our method as a parameter to the FilterDelegate parameter of the RunNumbersThroughFilter method, and FilterDelegate takes an int. I don’t really need to specify the variable type of _number:

I am allowed to keep the parentheses of this expression: (_number), but, really, parenthesis in that context would only allow me to put more than one argument. Since I only have the _number argument, I don’t really need those either:

Now, the return keyword, I only have a single expression inside this method, so, the compiler can infer what to return too. I can remove it:

If my method would have had more than one statement, I would have been forced to write the return statement, so that the compiler can understand which of the statements to return.

I just said we only have a single statement, and you can see that we are using curly brackets. You do remember that curly brackets only define a block of code that allows us to write multiple statements, right? So, if I have a single statement, I can remove them as well, and I can also remove the semicolon at the end of my statement, since a semicolon only separates multiple statements one from another:

In this stage, you might thing that I’ve chopped up my method so much that there’s not much left of it. You are right, and that is actually all that we need from that method. We need _number, since it is the argument name that we use to pass parameters to our chopped function, and the expression itself, _number < 5, that uses that named argument and returns a value. It might be obvious that the compiler needs to know where the argument(s) of our method ends and where its body begins, and we can’t use a comma or something of the sort, because it might appear that we are passing yet another parameter, separated by comma. Instead, C# offers us a special symbol that it inherited from other functional languages, an equal sign followed by a right arrow:

This piece of code is an actual lambda expression: _number => _number < 5. Let me translate its syntax: “use _number variable as a method parameter, send this parameter to this expression, and give me the value that the expression returns”. Or, the shortened form, “use _number and tell me when _number is less than five”. Or, the shortest form, “_number where _number is less that 5″.

If I integrate it in my codes at this stage:

I still get the numbers that are less than five:

It is great that we are not forced to declare the three methods at the top of our class. Now, we only change a sign and a value, in order to complete change what numbers we filter.

If you ever need a lambda expression that takes no arguments, you are forced to specify two brackets, like this:

In this case, I declared i variable outside the lambda expression, in the body of my Main method. Therefor, I didn’t need to declare a lambda expression that takes any parameters, since inside its body I am incrementing the value declared externally. Still, I still had to use () when I declared the lambda expression, so that the compiler understands that it has no parameters.

If you want to know what happens under the hood, lambda expressions don’t work in MSIL. When the compiler sees a lambda expression, it will actually convert it to a normal method:

C# Lambda Expression MSIL generated methodSo, when thinking of lambda expressions, think of them as methods. But, the cool thing about is think about them as methods without names. We don’t care about the name, because we are only using them once, where they are declared. That is the reason some programmers call them anonymous methods, even though anonymous methods are something different in C#.

Comments

comments

Tags: , , , , ,

Leave a Reply



Do NOT follow this link or you will be banned from the site!