Lambda Expressions

A lambda is a method written inline with =>, in either expression or statement form, with its parameter types usually inferred.

Syntax(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

Example · csharp
// 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 42

When 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.

Example · csharp
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:low

A 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.

Example · csharp
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
// two

A 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.

Example · csharp
// 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

  • Be the first to comment on this lesson.