1. Introduction
You might not have known this, but one of the favorite interview questions is “What do you know about lambda expressions?”. Why do they ask about them? Because it's a very handy and "trendy" feature in C# that lets you write less code and make it more expressive. If anonymous methods are like handwriting a letter, lambda expressions are like sending a message in a messenger: fast, compact, and sometimes even with emojis (well, almost).
Lambda expressions came to C# in version 3.0 and since then they've spread everywhere: in LINQ, event handlers, threads, collections, and even AI libraries on .NET.
Lambda expression — a way to declare a method right in the code "in-place", without a name, super short and clear. The basic syntax always looks like this:
(parameters) => expression_or_block_of_code
This "chevron" => is called the “lambda operator” or “arrow”.
A small analogy
Remember how you write a recipe: “Take an apple, cut it, put it in a bowl”.
In C# that would be:
(yabloko) => porezat(yabloko)
2. Converting an anonymous method to a lambda
Say we have a delegate:
delegate int SquareDelegate(int x);
An anonymous method would look like this:
SquareDelegate sq = delegate(int x) {
return x * x;
};
And now the same with a lambda expression:
SquareDelegate sq = (int x) => { return x * x; };
But C# can infer types, so we can shorten it further:
SquareDelegate sq = x => x * x;
Awesome compactness!
Comparison with a "regular" method and an anonymous function
| Way | Declaration | Where you can use it | Cons |
|---|---|---|---|
| Named method | In a class | Everywhere | Needs a name, more code |
| Anonymous method | In code | Only with delegates | More verbose syntax |
| Lambda expression | In code | Anywhere with delegates | Type can be unclear sometimes |
3. Lambda syntax: variations
Parameters
No parameters:
Action hello = () => Console.WriteLine("Privet, mir!");
One parameter (you can omit parentheses):
Func<int, int> inc = x => x + 1;
Multiple parameters (parentheses required):
Func<int, int, int> sum = (a, b) => a + b;
Lambda body
Single expression — no braces and no return:
x => x * x
Block of code — braces, return is needed:
(x, y) =>
{
int z = x + y;
return z * z;
}
Parameter types
Most of the time you don't need to specify types — the compiler will infer them. But if you want, go ahead:
(x, y) => x + y // Compiler infers types.
(int x, int y) => x + y // You can be explicit.
4. Nuances and more realistic examples
Using with collections
Remember our small learning app (for example, a list of users)? Suppose we have an array of numbers:
int[] numbers = { 1, 2, 3, 4, 5 };
We need to pick only even numbers:
var evenNumbers = numbers.Where(n => n % 2 == 0);
Here Where is a LINQ extension method, and the condition is our lambda filter.
Passing into a method
Say we have a delegate-method:
public delegate bool Filter(int number);
public static int[] FilterNumbers(int[] data, Filter predicate)
{
var result = new List<int>();
foreach (var n in data)
if (predicate(n))
result.Add(n);
return result.ToArray();
}
Now pass a lambda:
int[] evens = FilterNumbers(numbers, n => n % 2 == 0);
Lambda as an event handler
button.Click += (sender, args) => Console.WriteLine("Knopka nazhata!");
5. Lambdas and standard generic delegates
It's hard to imagine lambdas without Func<>, Action<> and Predicate<>. These are special delegate types:
- Action — returns nothing.
- Func — returns a value.
- Predicate — returns bool, usually for filtering.
Example with Action:
Action<string> log = message => Console.WriteLine(message);
log("Eto soobshchenie cherez lyambdu!");
Example with Func:
Func<int, int, int> multiply = (a, b) => a * b;
int product = multiply(3, 5); // 15
Example with Predicate:
Predicate<int> isNegative = n => n < 0;
bool test = isNegative(-7); // true
6. Lambdas with multiple expressions: when you need a block
If a lambda must perform several operations, use braces and an explicit return:
Func<int, int, string> describeSum = (a, b) =>
{
int sum = a + b;
return $"Summa: {sum}";
};
Without return the compiler will complain (but won't explain well — it will just say that "not all code paths return a value").
7. Returning void: Action
When a lambda returns nothing, use Action:
Action greet = () => Console.WriteLine("Ulybaytes' — kod compilyuetsya!");
A lambda can contain as many statements as you like:
Action<int> printSquare = x =>
{
int sq = x * x;
Console.WriteLine($"Kvadrat chisla {x} raven {sq}");
};
8. Limitations and common mistakes
Sometimes you want to write a lambda very freely, but the compiler won't always agree.
You can't declare a variable with the same name as one captured from outside.
Watch your types: if the delegate signature and the lambda don't match — you'll get an error.
Returning a value is required if the delegate expects one.
In complex lambdas don't forget braces. Single expression — no braces and no return. Multiple statements — braces and return.
GO TO FULL VERSION