100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
C#

Lambda Expressions

Lambda expressions provide concise, inline syntax for anonymous functions, forming the backbone of LINQ, delegates, and functional-style C# code.

LINQ & Functional FeaturesIntermediate8 min readJul 9, 2026
Analogies

Lambda Expressions

A lambda expression is a concise way to write an anonymous function inline, using the => ('goes to') operator to separate parameters from the body. Lambdas can be assigned to a delegate type (like Func<T, TResult> or Action<T>) or to an Expression<TDelegate>, and are the mechanism that makes LINQ, event handlers, and callback-based APIs readable without declaring a separate named method for every small operation. C# infers parameter types from context whenever possible, so you rarely need to annotate them explicitly in typical LINQ usage.

🏏

Cricket analogy: A lambda like runs => runs * 2 for a boundary bonus is like a scorer scribbling a quick one-off calculation on a napkin instead of writing a whole new rulebook chapter just to double a boundary's value for one over.

Syntax Forms

Lambdas come in expression-bodied form, x => x * x, which implicitly returns the expression's value, and statement-bodied form, x => { var y = x * x; return y; }, which allows multiple statements inside braces with an explicit return. Parameter lists can omit types (implicit), include them explicitly, or use var (C# 10+) for clarity in complex generic contexts. A parameterless lambda is written () => ..., and multiple parameters are wrapped in parentheses: (x, y) => x + y.

🏏

Cricket analogy: x => x * x is like a quick single-line note in the scorebook margin, 'double it', while a statement-bodied lambda with braces is like a fuller scoring annotation with several steps, ending in an explicit recorded result.

csharp
Func<int, int, int> add = (x, y) => x + y;
Action<string> log = message => Console.WriteLine($"[LOG] {message}");
Predicate<int> isEven = n => n % 2 == 0;

// Statement-bodied lambda with multiple statements
Func<int, string> classify = n =>
{
    if (n < 0) return "negative";
    if (n == 0) return "zero";
    return "positive";
};

// Lambdas used with LINQ operators
var numbers = Enumerable.Range(-3, 7).ToList();
var positives = numbers.Where(n => n > 0).ToList();

// C# 12: natural type inference for lambdas assigned to var
var square = (int n) => n * n;

Console.WriteLine(add(2, 3));
Console.WriteLine(classify(-5));

Closures and Captured Variables

Lambdas can capture variables from their enclosing scope, forming a closure. Captured variables are not copied by value at the moment the lambda is created — instead, the lambda holds a reference to the variable itself, so if the outer variable changes after the lambda is defined but before it's invoked, the lambda sees the updated value. This is especially important inside loops, where capturing the loop variable incorrectly (a mistake more common in older C# versions, though C# 5+ fixed foreach's per-iteration scoping) can lead to every closure seeing the same final value.

🏏

Cricket analogy: A lambda capturing the current over count as a closure is like a scorer's running tally that always reflects the latest over, not a frozen snapshot from when the tally was first written down at the start of play.

A lambda can be compiled either to a delegate (an actual executable function pointer) or to an Expression<TDelegate> (a data structure describing the lambda's logic as an expression tree). EF Core relies on the latter to translate LINQ predicates into SQL — this is why arbitrary C# method calls inside an EF query sometimes fail to translate: the expression tree has no way to represent them.

Capturing a mutable field or loop-created disposable object inside a lambda that outlives the current iteration (e.g., stored in a list of callbacks) can cause unexpected shared state or use of a disposed object. Prefer capturing a local copy inside the loop body if each lambda needs its own independent value.

  • Lambda expressions are anonymous, inline functions written with the => operator.
  • They can be expression-bodied (single expression, implicit return) or statement-bodied (block with explicit return).
  • Lambdas can compile to delegates (Func/Action/Predicate) or to Expression<TDelegate> for expression-tree-based APIs like EF Core.
  • Closures let lambdas capture enclosing variables by reference, not by value snapshot.
  • Since C# 5, foreach gives each iteration its own loop variable, avoiding the classic closure-capture-in-loop bug.
  • Type inference usually removes the need to annotate lambda parameter types explicitly.

Practice what you learned

Was this page helpful?

Topics covered

#CStudyNotes#Programming#LambdaExpressions#Lambda#Expressions#Syntax#Forms#Functions#StudyNotes#SkillVeris