C# Lambda Expressions
A lambda expression is a short, anonymous function you write inline — without giving it a name or a separate method block. Lambdas make your code shorter and more readable, especially when working with collections, events, and delegates.
The => Arrow Operator
Lambdas use the => symbol, which reads as "goes to" or "returns." The left side lists parameters; the right side is the expression or body.
// Regular method:
int Square(int x)
{
return x * x;
}
// Equivalent lambda:
x => x * x
Lambda Anatomy Diagram
┌────────────────────────────────────────────────────────────┐
│ LAMBDA EXPRESSION ANATOMY │
├────────────────────────────────────────────────────────────┤
│ │
│ x => x * x │
│ │ │ │ │
│ │ │ └── expression (the result) │
│ │ └──── "goes to" operator │
│ └── parameter │
│ │
│ Multiple parameters: │
│ (x, y) => x + y │
│ │
│ No parameters: │
│ () => Console.WriteLine("Hello") │
│ │
│ Block body: │
│ x => { int result = x * 2; return result; } │
│ │
└────────────────────────────────────────────────────────────┘
Lambdas With Func and Action
Lambdas are commonly stored in Func<> and Action<> delegate types.
Func — returns a value
// Func<input, output>
Func<int, int> square = x => x * x;
Console.WriteLine(square(5)); // 25
Func<int, int, int> add = (a, b) => a + b;
Console.WriteLine(add(3, 4)); // 7
Func<string, string> greet = name => "Hello, " + name;
Console.WriteLine(greet("Alice")); // Hello, Alice
Action — no return value
// Action<input>
Action<string> print = msg => Console.WriteLine(msg);
print("Learning C#!"); // Learning C#!
Action<int, int> showSum = (a, b) => Console.WriteLine("Sum: " + (a + b));
showSum(10, 5); // Sum: 15
Lambdas with List Methods
Lambdas shine when used with collection methods like Find, FindAll, Where, Select, and Sort.
Find and Filter
List<int> numbers = new List<int>() { 3, 15, 7, 22, 10, 4 };
// Find first number greater than 10:
int first = numbers.Find(n => n > 10);
Console.WriteLine(first); // 15
// Find all numbers greater than 10:
List<int> big = numbers.FindAll(n => n > 10);
// big = [15, 22]
// Remove all even numbers:
numbers.RemoveAll(n => n % 2 == 0);
// numbers = [3, 15, 7]
Sort with Custom Logic
List<string> names = new List<string>() { "Carol", "Alice", "Bob" };
// Sort by length of name:
names.Sort((a, b) => a.Length.CompareTo(b.Length));
// [Bob, Alice, Carol]
// Sort alphabetically:
names.Sort((a, b) => string.Compare(a, b));
// [Alice, Bob, Carol]
ForEach
List<int> scores = new List<int>() { 80, 90, 70 };
scores.ForEach(s => Console.WriteLine("Score: " + s));
// Score: 80
// Score: 90
// Score: 70
Lambdas in LINQ
Lambdas are the backbone of LINQ (Language Integrated Query). They let you filter, sort, and transform data in a readable style.
using System.Linq;
List<int> data = new List<int>() { 5, 12, 3, 8, 20, 1, 17 };
// Where = filter
var evens = data.Where(n => n % 2 == 0);
// [12, 8, 20]
// Select = transform
var doubled = data.Select(n => n * 2);
// [10, 24, 6, 16, 40, 2, 34]
// OrderBy = sort
var sorted = data.OrderBy(n => n);
// [1, 3, 5, 8, 12, 17, 20]
// Chained:
var result = data
.Where(n => n > 5)
.OrderBy(n => n)
.Select(n => n * 10);
// [80, 120, 170, 200]
Block Body Lambdas
When your lambda needs more than one statement, use curly braces and a return keyword.
Func<int, string> classify = n =>
{
if (n < 0) return "Negative";
if (n == 0) return "Zero";
return "Positive";
};
Console.WriteLine(classify(-5)); // Negative
Console.WriteLine(classify(0)); // Zero
Console.WriteLine(classify(7)); // Positive
Capturing Variables — Closures
A lambda can capture and use variables from the surrounding scope. This is called a closure.
int multiplier = 3; Func<int, int> times = x => x * multiplier; Console.WriteLine(times(4)); // 12 Console.WriteLine(times(7)); // 21 multiplier = 5; // change the captured variable Console.WriteLine(times(4)); // 20 ← uses new value!
Closure Diagram
┌────────────────────────────────────────────────────────────┐ │ int multiplier = 3; │ │ Func<int,int> times = x => x * multiplier; │ │ ↑ │ │ captures outer variable │ │ │ │ Lambda "remembers" multiplier even when called later │ └────────────────────────────────────────────────────────────┘
Lambda vs Regular Method
┌──────────────────────────────┬───────────────────────────────┐ │ Regular Method │ Lambda │ ├──────────────────────────────┼───────────────────────────────┤ │ Has a name │ Anonymous (no name) │ ├──────────────────────────────┼───────────────────────────────┤ │ Defined separately │ Written inline │ ├──────────────────────────────┼───────────────────────────────┤ │ Reusable across the program │ Typically used once │ ├──────────────────────────────┼───────────────────────────────┤ │ Better for complex logic │ Better for short operations │ └──────────────────────────────┴───────────────────────────────┘
Quick Reference
┌────────────────────────────────────────────────────────────┐
│ Basic lambda: x => x * x │
│ Two params: (a, b) => a + b │
│ No params: () => Console.WriteLine("Hi") │
│ Block body: x => { ...; return value; } │
│ Func (returns): Func<int, int> f = x => x + 1; │
│ Action (no return):Action<string> a = s => Print(s); │
└────────────────────────────────────────────────────────────┘
Lambda expressions make code dramatically more concise. Once you are comfortable reading the => arrow, you will find lambdas everywhere in C# — in LINQ queries, event handlers, sorting, filtering, and any place where a short function is needed without the overhead of a full method declaration.
