Codectionary / Developer documentation / C#

Lambda Expressions

A lambda expression is a compact way to represent an anonymous function - a block of code that can be passed around as a value, using the => (goes to) operator. Lambdas work with delegate types and functional interfaces like Func<> and Action<>, letting you write inline logic without a full named method. Lambdas are the foundation that makes LINQ's fluent, expressive query syntax possible.

Syntax

(parameters) => expression

Examples

Basic Lambda Syntax

Comparing a lambda to the equivalent named method it replaces.

// Old way: a named method
int SquareMethod(int x) => x * x;

// New way: a lambda assigned to a Func delegate
Func<int, int> square = x => x * x;

Console.WriteLine(square(5));  // 25
Console.WriteLine(SquareMethod(5));  // 25

Func, Action, and Predicate

The three most common built-in delegate types used with lambdas.

Func<int, int, int> add = (a, b) => a + b;       // takes 2 ints, returns an int
Console.WriteLine(add(3, 4));  // 7

Action<string> print = message => Console.WriteLine($"Log: {message}");
print("Hello");                                    // takes a string, returns nothing

Predicate<int> isEven = n => n % 2 == 0;
Console.WriteLine(isEven(4));  // true            // takes a value, returns bool

Multi-Statement Lambdas

A lambda body can contain multiple statements using curly braces and an explicit return.

Func<int, string> classify = n =>
{
    if (n < 0) return "negative";
    else if (n == 0) return "zero";
    else return "positive";
};

Console.WriteLine(classify(-5));  // negative
Console.WriteLine(classify(0));    // zero

Lambdas with Sorting and Collections

A very common use: passing a lambda to control sort order or filter a collection.

List<string> names = new List<string> { "Charlie", "Alice", "Bob" };

names.Sort((a, b) => a.CompareTo(b));
Console.WriteLine(string.Join(", ", names));  // Alice, Bob, Charlie

names.Sort((a, b) => b.Length - a.Length);  // longest name first
Console.WriteLine(string.Join(", ", names));

Best practices

  • Use lambdas for short, simple implementations passed as arguments - switch to a full local function or method once the logic gets long or complex
  • Use Func<> when the lambda returns a value, Action<> when it does not, and Predicate<> specifically for a single bool-returning test
  • Keep lambda parameter names short but meaningful, especially in LINQ chains where context is often clear from position
  • Remember lambdas capture variables from their enclosing scope (closures) - be mindful of this when a lambda outlives the loop iteration that created it

At a glance

Purpose
Applications on the .NET platform
File extension
.cs
Runs in
.NET runtime
Usually used with
.NET SDK and libraries

Specifications & further reading

Related C# documentation

LINQ Basics: Where & Select
LINQ (Language Integrated Query) provides a consistent, declarative way to query collections, databases, and XML directly within C# syntax. Where() filters a sequence down to elements matching a condition, and Select() transforms each element into something new (a projection). Both are 'lazy' - they build a query that only actually runs when you iterate over the result or call something like ToList().
LINQ Ordering & Grouping
OrderBy() and OrderByDescending() sort a sequence by a key you specify, with ThenBy() for secondary sort keys on ties. GroupBy() splits a sequence into groups based on a key selector, producing a sequence of groups where each group itself is enumerable and has a Key property identifying which group it represents.
LINQ Aggregation: Sum, Count, Average
LINQ provides built-in aggregation methods that reduce a sequence down to a single summary value: Count() for the number of elements, Sum() for a total, Average() for the mean, and Max()/Min() for extremes. Aggregate() is the general-purpose version, letting you define fully custom accumulation logic when the built-in methods aren't enough.
LINQ Element & Quantifier Operators
Element operators retrieve a single item from a sequence: First()/FirstOrDefault() for the first match, Single()/SingleOrDefault() for exactly one match, with 'OrDefault' variants returning a default value instead of throwing when nothing matches. Quantifier operators return a bool describing the sequence as a whole: Any() checks if at least one element matches, and All() checks if every element matches.