Lambda expressions
In the previous lesson, every method we passed was first declared on its own and given a name: Twice, Square, IsEven. For a one-line calculation needed in one place of the program, that is extra work.
The same goes for a recipe card. To hand a friend a short recipe, you do not have to enter it into a cookbook under a title first. You can write it right on the card, at the moment you hand it over.
In C# such a card is written right where a delegate is needed:
Func<int, int> twice = x => x * 2;
Console.WriteLine(twice(5)); // 10
List<int> nums = [4, -1, 6, -3];
nums.RemoveAll(n => n < 0);
Console.WriteLine(string.Join(" ", nums)); // 4 6Let's go through the program line by line:
x => x * 2is a method without a name: it takesxand returnsx * 2. It goes straight into the variabletwice.twice(5)calls it like any delegate and prints10.nums.RemoveAll(n => n < 0)gets the check right in the argument. The list method calls it for each item and removes those for which it returnedtrue.- The list keeps
4and6.
How a lambda is built
The notation x => x * 2 is called a lambda expression, or a lambda: a method without a name, written right where it is needed. It has three parts:
xto the left of=>is the parameter;=>is the same arrow as in short methods such asint Twice(int x) => x * 2;;x * 2to the right is the body: the lambda returns its value.
The parameter's type is not written in the lambda. The compiler takes it from the delegate the lambda goes into. Func<int, int> takes an int, so x is an int too. And RemoveAll on a List<int> expects a Predicate<int>, so n is also an int.
Interviewers like to ask this: what is a lambda expression? The answer: a method without a name, written right inside an expression and assigned to a delegate or passed to a method.
Forms of a lambda
A lambda can do what a method does: take several parameters or none, run several statements. Each case has its own form:
Func<int, int, int> add = (a, b) => a + b;
Func<int> answer = () => 42;
Func<int, int> next = x =>
{
int y = x + 1;
return y * 10;
};
Action<string> greet = name => Console.WriteLine($"Hi, {name}!");
Console.WriteLine(add(2, 3)); // 5
Console.WriteLine(answer()); // 42
Console.WriteLine(next(4)); // 50
greet("Ann"); // Hi, Ann!One parameter
A single parameter needs no parentheses: x => x * 2. This is the most common form.
Several parameters or none
Parameters are listed in parentheses, separated by commas: (a, b) => a + b. A lambda with no parameters starts with empty parentheses: () => 42.
A body of several statements
When one line is not enough, the body goes in braces, as in a method. Then return gives back the value: the lambda next first computes y and then returns y * 10. A lambda for an Action returns nothing, so it needs no return.
Explicit parameter types
You can write the parameter's type yourself: (int x) => x * 2. Then the parentheses are needed even around a single parameter. More often the type is left out, and the delegate supplies it.
A card without a cookbook
Here the recipe card analogy ends. A recipe on a card makes sense on its own, but a lambda needs a delegate. Without one, the compiler knows neither the parameter type nor the return type: x => x * 2 fits an int as well as a double.
Common mistake
The lambda was put into a variable declared with var:
var twice = x => x * 2; // ✗
Console.WriteLine(twice(5));error CS8917: The delegate type could not be inferred.The program does not compile. There is not a single type to the right of =, and the compiler has no way to learn what x is. There are two fixes:
- Name the delegate:
Func<int, int> twice = x => x * 2;. - Give the parameter a type:
var twice = (int x) => x * 2;. Then the compiler picks the typeFunc<int, int>for the variable itself.
Key points
- A lambda is a method without a name, written right where it is needed:
x => x * 2. - Parameters go to the left of
=>, the body to the right; several parameters or none go in parentheses, several statements in braces withreturn. - A lambda gets its parameter and return types from the delegate, so
var twice = x => x * 2;does not compile.