C# Expression Lambda: Syntax and Practical Usage
c# expression lambda: Learn C# expression lambda syntax, how it differs from statement lambdas, and where it fits in LINQ, delegates, and expression trees.
c# expression lambda requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
When you write a LINQ query like products.Where(p => p.Price > 100), the p => p.Price > 100 part is an expression lambda. It is the most common form of lambda in C# because it is concise and directly expresses a transformation or condition. Understanding its syntax and behavior is essential for writing idiomatic C# and for knowing when a statement lambda or a method group is a better choice.
Expression Lambda Syntax and the Arrow Operator
An expression lambda has the form:
(input parameters) => expression
The left side declares the parameters, and the right side is a single expression that is evaluated and returned. Parentheses are optional when there is exactly one parameter and the type can be inferred:
Func<int, int> square = x => x * x;
With multiple parameters, parentheses are required:
Func<int, int, int> add = (a, b) => a + b;
Explicit types are allowed when the compiler cannot infer them, such as when the delegate type is not fully known:
Func<int, string> toString = (int x) => x.ToString();
The expression can be a method call, a property access, a conditional, or any expression that yields a value. It cannot contain statements like if, return, or variable declarations. Those require a statement lambda.
Expression Lambdas vs Statement Lambdas
A statement lambda uses braces and can contain multiple statements:
Func<int, int> factorial = n => { int result = 1; for (int i = 2; i <= n; i++) result *= i; return result; };
Statement lambdas are useful when the logic is too complex for a single expression, but they have a key limitation: they cannot be converted into expression trees. The C# compiler only generates expression trees from expression lambdas. If you need to inspect or translate the lambda at runtime, you must use an expression lambda.
For simple transformations, an expression lambda is more readable and often more efficient because it avoids the overhead of a block body. The choice is not about performance in most cases, but about what the code communicates. If the logic fits in one line, an expression lambda is usually clearer.
Using Expression Lambdas with LINQ and Delegates
The most common use of expression lambdas is with LINQ methods that accept Func<T, TResult> delegates. For example:
var expensiveProducts = products.Where(p => p.Price > 100);
Here, the lambda is compiled to a delegate and executed in memory. The same syntax works with Select, OrderBy, Any, and many other extension methods.
Expression lambdas also work with custom delegate types. For instance:
public delegate bool Filter(int value); Filter isPositive = value => value > 0;
The compiler infers the types and generates a method that returns the expression's result. This makes the code feel natural and reduces boilerplate compared to writing separate named methods.
One subtlety is that the expression is evaluated immediately when the delegate is invoked. There is no deferred execution unless the lambda is used in a query that returns IEnumerable<T> with deferred semantics, such as Where or Select.
Expression Trees and Runtime Compilation
When you assign an expression lambda to a parameter of type Expression<TDelegate>, the compiler does not create a delegate. Instead, it builds an expression tree that represents the code as data:
Expression<Func<int, int>> expr = x => x * 2;
This tree can be inspected, modified, and compiled at runtime. Frameworks like Entity Framework use this to translate C# expressions into SQL. For example, Where(p => p.Price > 100) becomes a SQL WHERE clause when the query provider processes the expression tree.
The same syntax produces very different runtime behavior depending on whether the target is a delegate or an expression tree. This is why you cannot use statement lambdas with IQueryable providers that rely on expression trees. If you need to pass a complex block of logic to a query provider, you must refactor it into an expression lambda or use a different approach.
Performance and Allocation Considerations
Expression lambdas themselves have minimal overhead. The compiler generates a method and possibly a closure object if the lambda captures variables from the surrounding scope. For example:
int threshold = 100; var result = products.Where(p => p.Price > threshold);
The lambda captures threshold, so the compiler creates a closure class to hold that value. This allocation is small and usually not a concern. However, in hot paths where a lambda is created repeatedly, the closure allocation can add pressure on the garbage collector.
One way to avoid repeated allocations is to cache the delegate as a static field:
private static readonly Func<Product, bool> IsExpensive = p => p.Price > 100;
Then reuse that delegate in multiple calls. This is a micro-optimization and should only be applied after profiling shows it matters. The bigger performance consideration is whether the lambda is used with an expression tree provider that performs translation. That translation cost can be significant, but it is inherent to the provider, not the lambda syntax.
Common Mistakes and Maintainability Concerns
A frequent mistake is using a statement lambda when an expression lambda is required, especially with IQueryable:
// This will not compile if the provider requires an expression tree var data = db.Products.Where(p => { return p.Price > 100; });
This fails because the block body prevents conversion to an expression tree. The fix is to write the logic as a single expression:
var data = db.Products.Where(p => p.Price > 100);
Another issue is overusing lambdas for complex logic that would be clearer as a named method. If the expression spans multiple lines or contains nested conditionals, a local function or a private method often improves readability. Expression lambdas are best when the logic is short and the intent is obvious.
Also, be careful when capturing loop variables. In older C# versions, a lambda inside a for loop captured the same variable, leading to unexpected results. This was fixed in C# 5, but the behavior is still worth understanding if you work with legacy code.
When to Choose an Expression Lambda Over Other Approaches
Expression lambdas are the right choice when you need a short, inline function that can be passed to a delegate or an expression tree. They are ideal for LINQ queries, event handlers, and configuration callbacks.
Use a statement lambda when the logic requires multiple statements. Use a named method when the logic is reused in several places or is too long to read comfortably in a lambda. Use a method group when you already have a method with a compatible signature:
Func<int, int> f = Math.Abs;
Method groups are often more readable and avoid the extra syntax. The decision comes down to clarity and the specific requirement of the API you are calling. If the API expects an Expression<T>, you must use an expression lambda. If it expects a delegate, you have more freedom.
Expression lambdas are also the foundation for building expression trees manually. When you need to generate code at runtime, you can compose Expression nodes, but starting from a lambda is often simpler. The Compile method turns an expression tree back into a delegate:
Expression<Func<int, int>> expr = x => x + 1; Func<int, int> compiled = expr.Compile(); int result = compiled(41); // 42
This pattern is useful in dynamic scenarios, but it adds a compilation cost. Use it only when the expression tree provides a benefit, such as translating to another language or modifying behavior dynamically.