Lambda Expressions
A lambda is a method written inline with =>, in either expression or statement form, with its parameter types usually inferred.
(parameters) => expression | (parameters) => { statements; return value; }A lambda expression is a method written inline, at the point where it is used, with no name. The => token separates its parameters from its body. You convert a lambda to a delegate type, so anywhere a delegate is wanted a lambda can be written instead of a named method.
Two body forms
An expression lambda has one expression as its body and returns that expression's value: x => x * x. A statement lambda has a braced block and must use return to produce a value: x => { int y = x * x; return y; }. The first is shorthand for the second when the body is a single expression; both compile to the same thing.
One parameter needs no parentheses. Zero, or two or more, do: () => 0 and (a, b) => a + b. A parameter you do not use can be written _.
Types
Parameter types are normally inferred from the delegate being assigned to. You may write them out instead: (int x, string s) => s.Length > x. They must be all explicit or all inferred; mixing them is error CS0748. Since C# 10 a lambda with enough type information has a natural type, so var parse = (string s) => int.Parse(s); infers Func<string, int>.
Modifiers
Since C# 9, static x => x * x forbids the lambda from touching any surrounding local variable. C# 12 allows default parameter values on an explicitly typed parameter list. C# 14 lets you put ref, out, in, scoped or ref readonly on a parameter without writing its type; params still requires the type.
Example
// Expression body: the value of the expression is the return value.
Func<int, int> square = x => x * x;
// Statement body: braces, so `return` is required. Same meaning, longer form.
Func<int, string> describe = x =>
{
string label = x % 2 == 0 ? "even" : "odd";
return $"{x} is {label}";
};
// Two parameters, with a discard for the one that is not used.
Func<int, int, int> firstOnly = (first, _) => first;
Console.WriteLine(square(7));
Console.WriteLine(describe(7));
Console.WriteLine(firstOnly(1, 2));
// C# 14: `out` on a lambda parameter no longer forces you to write its type.
// The longhand, valid in every version, is (string text, out int result).
TryParse<int> parse = (text, out result) => int.TryParse(text, out result);
Console.WriteLine(parse("42", out int value) ? $"parsed {value}" : "not a number");
delegate bool TryParse<T>(string text, out T result);
// Output:
// 49
// 7 is odd
// 1
// parsed 42When to use it
- Filter and project a collection with LINQ without writing named methods for each step.
- Give OrderBy a one-line key selector.
- Write a short event handler right where you subscribe.
- Supply a factory to a cache lookup so the value is built only on a miss.
More examples
Both body forms in one LINQ chain
The everyday use of lambdas. Keep the expression form while the body is one expression; switch to braces when you need a local variable or more than one statement.
int[] prices = [120, 45, 300, 75];
// Expression lambda: one expression, no return keyword.
IEnumerable<int> cheap = prices.Where(p => p < 100);
// Statement lambda: braces, so return is needed.
IEnumerable<string> labels = prices.Select(p =>
{
string band = p >= 100 ? "high" : "low";
return $"{p}:{band}";
});
Console.WriteLine(string.Join(" ", cheap));
Console.WriteLine(string.Join(" ", labels));
// Output:
// 45 75
// 120:high 45:low 300:high 75:lowA static lambda and an explicit return type
static (C# 9) is how you prove a lambda is self-contained, which matters for lambdas that are stored and reused. The explicit return type is the escape hatch when the two branches of a conditional have no common inferred type.
int factor = 3;
// `static` means this lambda may not use `factor` or any other local.
// The compiler reports an error if it tries.
Func<int, int> triple = static x => x * 3;
// An explicit return type goes before the parameter list, which must then
// be parenthesised. Without it the compiler cannot pick int or string.
var choose = object (bool b) => b ? 1 : "two";
Console.WriteLine(triple(factor));
Console.WriteLine(choose(true));
Console.WriteLine(choose(false));
// Output:
// 9
// 1
// twoA default parameter value on a lambda
A lambda with a default value has no matching Func type, so the compiler synthesises a delegate type for it. That is why this must be assigned to var or to a delegate type you declared with the same default.
// C# 12 and later. Default values need an explicitly typed parameter list.
var incrementBy = (int source, int increment = 1) => source + increment;
Console.WriteLine(incrementBy(5));
Console.WriteLine(incrementBy(5, 2));
// Output:
// 6
// 7
Discussion