Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

An expression lambda is a concise form of an anonymous function in C# that consists of input parameters, the lambda declaration operator (=>), and a single executable expression. Unlike statement lambdas, which enclose multiple statements within a lexical block ({}), expression lambdas evaluate the right-hand side expression and implicitly return its result.

Syntax

The fundamental syntax of an expression lambda is:
(input_parameters) => expression

Technical Mechanics

  • The Lambda Operator (=>): This operator separates the input parameters on the left side from the expression body on the right side. It is right-associative.
  • Implicit Return: If the target delegate signature expects a non-void return type (e.g., Func<T, TResult>), the compiler automatically returns the evaluated result of the expression. The return keyword is strictly prohibited in an expression lambda.
  • Void Evaluation: If the target delegate returns void (e.g., Action<T>), the expression must be a statement expression (such as a method invocation), and its return value (if any) is discarded.
  • Type Inference and Natural Types: Historically, the C# compiler inferred the types of the input parameters and the return type based strictly on the target type the lambda was assigned to. Starting in C# 10, lambdas possess a natural type. If the lambda provides enough type information (e.g., explicitly typed parameters), the compiler can infer its delegate type without a target type, allowing direct assignment to implicitly typed variables (var).
// Target-typed inference
System.Func<int, int> square = x => x * x; 

// Natural type inference (C# 10+)
var cube = (int x) => x * x * x; 

Parameter Declarations

The syntax for the left-hand side adapts based on the number and typing of parameters. To be valid C#, these expressions must be assigned to a variable, delegate, or passed as an argument:
// Zero parameters: Requires empty parentheses
System.Action beep = () => System.Console.Beep();

// Single parameter (Implicitly typed): Parentheses are optional
System.Func<int, int> square = x => x * x;

// Single parameter (Explicitly typed): Parentheses are mandatory
System.Func<int, int> increment = (int x) => x + 1;

// Multiple parameters: Parentheses and comma separation are mandatory
System.Func<int, int, int> add = (x, y) => x + y;

// Explicitly typed multiple parameters
System.Func<int, string, string> format = (int x, string y) => x.ToString() + y;

// Discards (C# 9.0+): Used when parameters are required by the signature but unused
System.Func<int, int, int> constant = (_, _) => 42;

Compilation Targets

A critical architectural feature of expression lambdas is that they can be compiled into two entirely distinct underlying types depending on the assignment target: 1. Delegates (Action, Func, or custom delegates) When assigned to a delegate type, the compiler emits Intermediate Language (IL) code that executes the expression. The specific code generation strategy depends on whether the lambda captures external state:
  • Stateless Lambdas: If no local variables are captured, modern Roslyn compilers generate an instance method on a hidden, compiler-generated singleton class (typically named <>c). The delegate allocation is then cached using this singleton instance (e.g., <>9) as its target, preventing unnecessary memory allocations on subsequent invocations.
  • Stateful Lambdas (Closures): If the lambda captures local variables, the compiler generates a separate nested class (e.g., <>c__DisplayClass) to hold both the generated method and the captured state as fields.
// Compiles to executable IL representing the lambda's logic
System.Func<int, int> square = x => x * x; 
2. Expression Trees (System.Linq.Expressions.Expression<TDelegate>) When assigned to an Expression<TDelegate>, the compiler does not emit IL that executes the lambda’s mathematical or business logic. Instead, it generates executable IL that invokes System.Linq.Expressions.Expression factory methods. When executed at runtime, this generated IL constructs an in-memory abstract syntax tree (AST) representing the lambda’s structure. This allows the logic to be inspected, modified, or translated into other domains at runtime.
// Compiles to IL that executes factory methods to build an AST at runtime
System.Linq.Expressions.Expression<System.Func<int, int>> squareTree = x => x * x;

Advanced Syntax Features

Explicit Return Types (C# 10+) If the compiler cannot infer the return type (e.g., due to ambiguous implicit conversions), you can declare an explicit return type before the parameter list.
var getObject = object (bool b) => b ? 1 : "string";
Tuple Returns An expression lambda can evaluate to and implicitly return a tuple. The tuple literal must be enclosed in parentheses.
var getCoordinates = (int x, int y) => (x + y, x - y);
Master C# with Deep Grasping Methodology!Learn More