What are lambda expressions and how do they work in C#?

8 minintermediate.NETlambdaLINQC#

Quick Answer

Lambda expressions are anonymous functions that provide concise syntax for delegates and expression trees. Syntax: (parameters) => expression. They support closures, can be used with LINQ, events, and functional programming. Use for simple operations, LINQ queries, and callbacks. Avoid for complex logic or when named methods would be clearer.

Detailed Answer

Lambda Expressions are anonymous functions that allow you to write inline code blocks that can be passed as arguments to methods or assigned to variables. They provide a concise way to represent delegates or expression trees.

Basic Lambda Syntax:

// Lambda expression syntax: (parameters) => expression
// Simple lambda
Func<int, int> square = x => x * x;
int result = square(5); // 25

// Lambda with multiple parameters
Func<int, int, int> add = (x, y) => x + y;
int sum = add(3, 4); // 7

// Lambda with no parameters
Func<string> getMessage = () => "Hello World";
string message = getMessage(); // "Hello World"

Lambda vs Anonymous Methods:

// Anonymous method (C# 2.0)
Func<int, int> oldWay = delegate(int x) { return x * x; };

// Lambda expression (C# 3.0+) - more concise
Func<int, int> newWay = x => x * x;

// Both do the same thing, but lambda is cleaner

Lambda with LINQ:

List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// Using lambda with LINQ methods
var evenNumbers = numbers.Where(x => x % 2 == 0);
var doubled = numbers.Select(x => x * 2);
var sum = numbers.Aggregate((x, y) => x + y);

// Lambda with complex expressions
var result = numbers
    .Where(x => x > 5)
    .Select(x => x * x)
    .OrderByDescending(x => x);

Lambda with Events:

public class Button
{
    public event EventHandler Click;
    
    protected virtual void OnClick()
    {
        Click?.Invoke(this, EventArgs.Empty);
    }
}

// Using lambda with events
Button button = new Button();
button.Click += (sender, e) => Console.WriteLine("Button clicked!");
button.Click += (sender, e) => MessageBox.Show("Hello!");

Lambda with Action and Func:

// Action - no return value
Action<string> printMessage = message => Console.WriteLine(message);
printMessage("Hello"); // Prints "Hello"

// Action with multiple parameters
Action<string, int> printMessageWithCount = (msg, count) => 
    Console.WriteLine($"{msg} (Count: {count})");

// Func - with return value
Func<int, int, int> multiply = (x, y) => x * y;
int product = multiply(3, 4); // 12

// Func with different return types
Func<string, int> getLength = str => str.Length;
int length = getLength("Hello"); // 5

Lambda with Complex Logic:

// Lambda with multiple statements (use braces)
Func<int, int> complexOperation = x =>
{
    int temp = x * 2;
    if (temp > 10)
        return temp + 5;
    else
        return temp - 2;
};

// Lambda with local variables
Func<int, int> factorial = n =>
{
    int result = 1;
    for (int i = 1; i <= n; i++)
        result *= i;
    return result;
};

Lambda with Predicates:

// Predicate<T> - returns bool
Predicate<int> isEven = x => x % 2 == 0;
bool result = isEven(4); // true

// Using with List<T>.FindAll
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
var evenNumbers = numbers.FindAll(x => x % 2 == 0); // [2, 4, 6]

Lambda with Custom Delegates:

// Custom delegate
public delegate int MathOperation(int x, int y);

// Using lambda with custom delegate
MathOperation add = (x, y) => x + y;
MathOperation multiply = (x, y) => x * y;

int sum = add(5, 3); // 8
int product = multiply(5, 3); // 15

Lambda with Expression Trees:

using System.Linq.Expressions;

// Expression tree - represents code as data
Expression<Func<int, int, int>> expression = (x, y) => x + y;

// Can be compiled to executable code
Func<int, int, int> compiled = expression.Compile();
int result = compiled(3, 4); // 7

// Can be analyzed and modified
BinaryExpression body = (BinaryExpression)expression.Body;
ParameterExpression left = (ParameterExpression)body.Left;
ParameterExpression right = (ParameterExpression)body.Right;

Lambda with Closures:

// Lambda captures variables from outer scope
int multiplier = 10;
Func<int, int> multiplyByTen = x => x * multiplier;

int result = multiplyByTen(5); // 50

// Changing the captured variable affects the lambda
multiplier = 20;
int newResult = multiplyByTen(5); // 100

Lambda with Async/Await:

// Async lambda
Func<Task<string>> asyncLambda = async () =>
{
    await Task.Delay(1000);
    return "Async result";
};

// Using async lambda
string result = await asyncLambda();

When to Use Lambda Expressions:

  1. LINQ operations - Where, Select, OrderBy, etc.
  2. Event handlers - Simple event handling
  3. Callback functions - Passing behavior as parameters
  4. Functional programming - Map, filter, reduce operations
  5. Short, simple operations - One-liner functions

When NOT to Use Lambda Expressions:

  1. Complex logic - Use regular methods instead
  2. Reusable code - Create named methods
  3. Performance-critical code - Regular methods might be faster
  4. Debugging - Harder to debug than named methods

Best Practices:

  1. Keep lambdas simple - avoid complex logic
  2. Use meaningful parameter names when possible
  3. Consider readability - don't sacrifice clarity for brevity
  4. Use parentheses for multiple parameters: (x, y) => x + y
  5. Use braces for multiple statements: x => { /* multiple statements */ }

Related Resources