Lambdas
A lambda in C# is an inline anonymous function: parameters => expression-or-block. The compiler infers the delegate or expression type from context, and captured variables are stored on a hidden closure object. Lambdas power LINQ, event handlers, async callbacks, and minimal-API endpoints in modern C#.
Lambdas, closures, expression trees, minimal APIs
EXAMPLE
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
// 1) Inline lambdas with LINQ
var nums = new[] { 1, 2, 3, 4, 5, 6 };
var evens = nums.Where(n => n % 2 == 0).Select(n => n * n);
Console.WriteLine(string.Join(",", evens)); // 4,16,36
// 2) Multi-statement lambda with explicit return
Func<string, string> normalise = s => {
if (string.IsNullOrWhiteSpace(s)) return "";
return s.Trim().ToLowerInvariant();
};
Console.WriteLine(normalise(" Hello WORLD ")); // hello world
// 3) Closures — captured variables persist with the lambda
Func<int, int> Counter() {
var n = 0;
return _ => ++n; // each call sees and mutates the same n
}
var bump = Counter();
Console.WriteLine($"{bump(0)} {bump(0)} {bump(0)}"); // 1 2 3
// 4) Local function vs lambda — local functions are usually faster
// and allow recursion without a workaround
int Fib(int n) => n < 2 ? n : Fib(n - 1) + Fib(n - 2);
Console.WriteLine(Fib(10)); // 55
// 5) Async lambdas
Func<int, Task<int>> Doubled = async x => {
await Task.Delay(10);
return x * 2;
};
Console.WriteLine(await Doubled(21)); // 42
// 6) Expression trees — the lambda is data, not code
// LINQ providers (EF Core) translate these to SQL.
Expression<Func<int, bool>> isAdult = age => age >= 18;
Console.WriteLine(isAdult); // age => (age >= 18)
Console.WriteLine(isAdult.Compile().Invoke(21)); // True
// 7) ASP.NET minimal APIs — endpoints ARE lambdas
// var app = WebApplication.CreateBuilder(args).Build();
// app.MapGet("/health", () => Results.Ok(new { ok = true }));
// app.MapPost("/orders", async (Order o, AppDb db) => {
// db.Orders.Add(o); await db.SaveChangesAsync();
// return Results.Created(\$"/orders/\{o.Id\}", o);
// });
// app.Run();
Why it matters
A lambda that captures a loop variable used to bite — pre-C# 5 every closure shared the same iteration variable. That was fixed long ago, but if you target older runtimes or use ref locals, copy the value into a local before the lambda to be safe.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
Func<int, int, int> add = (a, b) => a + b; Action<string> log = msg => Console.WriteLine(msg); log(add(2, 3).ToString());Try it Yourself »
Discussion
Loading…