- C# - Home
- C# - Overview
- C# - Environment
- C# - Program Structure
- C# - Basic Syntax
- C# - Data Types
- C# - Type Conversion
- C# - Variables
- C# - Constants
- C# - Operators
- C# - Arithmetic Operators
- C# - Assignment Operators
- C# - Relational Operators
- C# - Logical Operators
- C# - Bitwise Operators
- C# - Miscellaneous Operators
- C# - Operators Precedence
- C# Conditional Statements
- C# - Decision Making
- C# - If
- C# - If Else
- C# - Nested If
- C# - Switch
- C# - Nested Switch
- C# - Switch Expressions
- C# Control Statements
- C# - Loops
- C# - For Loop
- C# - While Loop
- C# - Do While Loop
- C# - Nested Loops
- C# - Break
- C# - Continue
- C# - Foreach Loop
- C# - Goto Statement
- C# OOP & Data Handling
- C# - Encapsulation
- C# - Methods
- C# - Nullables
- C# - Arrays
- C# - Strings
- C# - Structure
- C# - Enums
- C# - Classes
- C# - Inheritance
- C# - Polymorphism
- C# - Operator Overloading
- C# - Interfaces
- C# - Namespaces
- C# - Preprocessor Directives
- C# - Regular Expressions
- C# - Custom Exceptions
- C# - Exception Handling
- C# - File I/O
- C# Advanced Tutorial
- C# - Attributes
- C# - Reflection
- C# - Properties
- C# - Indexers
- C# - Delegates
- C# - Events
- C# - Collections
- C# - Generics
- C# - LINQ
- C# - IEnumerable vs IEnumerator
- C# - Anonymous Methods
- C# - Unsafe Codes
- C# - Tasks and Parallel Programming
- C# - Multithreading
- C# - Extension Methods
- C# - Lambda Expressions
- C# - Async and Await
- C# Modern Features
- C# - Tuples
- C# - Records
- C# - Pattern Matching Enhancements
- C# - Top-level Statements
- C# - Nullable Reference Types
- C# - What's New in C# 11 / 12 / 13
- C# - Global Usings
- C# - File-Scoped Namespaces
- C# Practical & Advanced Usage
- C# - JSON & XML Handling
- C# - Data Serialization & Deserialization
- C# - REST API Calls with Httpclient
- C# - Dependency Injection
- C# - Unit Testing with NUnit, xUnit & MSTest
- C# - Package Management with NuGet
Lambda Expressions in C#
Lambda expressions are a way to write small anonymous functions directly inside a program. Instead of creating a complete function, we can write the function directly where it is needed. Before lambda expressions, anonymous function or regular functions were used to do the same work.
In this chapter, we will learn about lambda expressions in C#, their syntax and how to use them. We will cover the following topics −
- Lambda Expression in C#
- Delegate Types for Lambda Expressions
- Lambdas with the Standard Query Operators
- Common Mistakes to Avoid
- Lambda Expression with No Parameters
- Lambda Expression with Multiple Parameters
- Lambda Expression with Return Keyword
- Closures in Lambda Expressions
Lambda Expression in C#
A lambda expression is a function without a name known as an anonymous function which accepts parameters and returns a value. These are short inline functions that we can define in place using the => operator. On the left side of the => are the input parameters and on the right side is the function body.
Lambda functions can be passed as arguments to methods, assigned to delegates, or used directly inside other methods without declaring them separately.
There are two types of lambda expressions −
We will discuss both types in detail below with examples.
Syntax of Lambda Expressions
Following is the syntax for defining a lambda expression −
(parameters) => expression
or
(parameters) => { statements }
Here, parameters are the inputs to the function. The first form is used for a single-line expression and the second form with { } is used for one or more statements. If there is only one parameter, the parentheses are optional. The => is called the lambda operator and { } contains the function body.
Expression Lambda
An Expression Lambda in C# is a lambda expression that contains a single expression and automatically returns its value. It is used for operations that can be written in one line such as calculations, filtering, or transformations and it does not require the return keyword.
Following is the syntax for defining an expression lambda −
(parameters) => expression
In this syntax, parameters are the input values for the lambda, the => operator separates the parameters from the body and expression is the operation or value that the lambda returns.
Example
Here is an example of an expression lambda where we add two numbers and return the result. We create a Func delegate, assign the lambda expression to it and then call it to get the result.
using System;
class Program
{
static void Main()
{
// Expression Lambda to add two numbers
Func<int, int, int> add = (x, y) => x + y;
int result = add(10, 15);
Console.WriteLine("The sum of 10 and 15 is " + result);
}
}
Following is the output of the above program which displays the sum of the two numbers.
The sum of 10 and 15 is 25
Statement Lambda
A Statement Lambda in C# contains one or more statements enclosed in { } and requires the return keyword if it returns a value. It is used for more complex operations, including multiple calculations, conditions, or additional logic that cannot be expressed in a single line.
Following is the syntax for defining a statement lambda −
(parameters) =>
{
// statements
return value; // if returning a value
}
In this syntax, parameters are the input values for the lambda, the => operator separates the parameters from the body and the statements inside { } perform the required operations. If the lambda returns a value, the return keyword must be used.
Example
Here is an example of a statement lambda where we take two numbers, print a message before performing the calculation, add them and return the result. We create a Func delegate, assign the lambda to it and then call it to get the sum.
using System;
class Program
{
static void Main()
{
// Statement Lambda to add two numbers with logging
Func<int, int, int> add = (x, y) =>
{
Console.WriteLine("Calculating the sum of " + x + " and " + y);
int sum = x + y;
return sum;
};
int result = add(20, 30);
Console.WriteLine("The sum of 20 and 30 is " + result);
}
}
Below we can see the output of the above program.
Calculating the sum of 20 and 30 The sum of 20 and 30 is 50
Delegate Types for Lambda Expressions
Lambda expressions can be directly assigned to delegates. A delegate is a type that holds a reference to a method which means we can pass methods as parameters to other methods, store them, or call them later.
C# provides three main built-in generic delegate types that are used with lambda expressions. We will learn about each type in detail below.
Func Delegate
The Func delegate represents a method that takes zero or more input parameters and returns a value. The last type parameter in a Func delegate always defines the return type, while the rest define the input parameters.
Syntax of Func Delegate
Following is the syntax for declaring a Func delegate −
Func<input1, input2, ..., returnType> delegateName = methodName;
Here, Func defines the delegate type. The angle brackets < > contain the input parameter types followed by the return type as the last element. The delegateName is the name of the delegate and methodName is the method assigned to it.
Example
Here's a complete example of a Func delegate. We create a Func<int, int, int> delegate where the first two int types are input parameters and the last int is the return type. We assign the Add method to this delegate and call it to get the sum.
using System;
class Program
{
static int Add(int a, int b)
{
return a + b;
}
static void Main()
{
Func<int, int, int> addNumbers = Add;
int result = addNumbers(10, 20);
Console.WriteLine("Sum: " + result);
}
}
Below is the output of the program which shows the calculated sum of the two numbers.
Sum: 30
Action Delegate
The Action delegate represents a method that can take zero or more input parameters but does not return any value.
Syntax of Action Delegate
Following is the syntax for declaring a Action Delegate -
Action<input1, input2, ...> delegateName = methodName;
Here, Action is the delegate type. The angle brackets < > contain the input parameter types. The delegateName is the name of the delegate and methodName is the method assigned to it.
Example
Here's an example of Action delegates where we create an Action<string> delegate that takes a single string parameter and does not return any value. We assign the DisplayMessage method to this delegate and then call it by passing a string argument to print the greeting.
using System;
class Program
{
static void DisplayMessage(string name)
{
Console.WriteLine("Hello, " + name + "!");
}
static void Main()
{
// Action delegate that takes a string parameter
Action<string> greet = DisplayMessage;
greet("tutorialspoint");
}
}
The output of the above program displays the greeting message as shown below −
Hello, tutorialspoint!
Predicate delegate
The Predicate delegate represents a method that takes a single input parameter and returns a boolean value either true or false.
Syntax of Predicate Delegate
Following is the syntax for declaring a Predicate delegate −
Predicate<inputType> delegateName = methodName;
Here, Predicate is the delegate type, <inputType> is the type of the input parameter, delegateName is the name of the delegate and methodName is the method assigned to it.
Example
Here's an example of a Predicate delegate where we create a Predicate<int> delegate named checkEven and assign it to the IsEven method. Then we use the list's Find() method to get the first number that satisfies the condition which displays the first even number in the list.
using System;
using System.Collections.Generic;
class Program
{
static bool IsEven(int number)
{
return number % 2 == 0;
}
static void Main()
{
Predicate<int> checkEven = IsEven;
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
int evenNumber = numbers.Find(checkEven);
Console.WriteLine("First even number: " + evenNumber);
}
}
Following is the output of the above program −
First even number: 2
Lambdas with the Standard Query Operators
Lambda expressions are used with Language Integrated Query (LINQ) methods such as Where(), Select(), OrderBy() and more to filter, transform, or sort data from collections like lists or arrays.
Let's look at a few examples to see how lambda expressions work with these LINQ methods.
Example 1: Using Where() to Filter Even Numbers
In this example, we use the Where() method with a lambda expression n =< n % 2 == 0 to filter all even numbers from a list. The lambda defines the condition for choosing elements and the result is stored in evenNumbers.
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var evenNumbers = numbers.Where(n => n % 2 == 0).ToList();
Console.WriteLine("Even Numbers:");
evenNumbers.ForEach(n => Console.Write(n + " "));
}
}
The output of the above program is shown below which displays the list of even numbers from the given list.
Even Numbers: 2 4 6 8 10
Example 2: Using Select() to Square Numbers
Here, we use the Select() method with the lambda expressionn => n * n to transform each number in the list by squaring it. The lambda defines how each element is projected into a new value and the results are stored in the squaredNumbers variable.
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
var squaredNumbers = numbers.Select(n => n * n).ToList();
Console.WriteLine("Squared Numbers:");
squaredNumbers.ForEach(n => Console.Write(n + " "));
}
}
Below is the output of the above program showing the squared values of the numbers in the list.
Squared Numbers: 1 4 9 16 25
Example 3: Using FirstOrDefault() to Find an Element
In this example, we use FirstOrDefault() method with a lambda expression f => f.StartsWith("M") to find the first element that starts with letter "M". The lambda defines the search condition and the result is stored in fruitWithM variable.
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
List<string> fruits = new List<string> { "Apple", "Banana", "Mango", "Orange" };
var fruitWithM = fruits.FirstOrDefault(f => f.StartsWith("M"));
Console.WriteLine("First fruit starting with M: " + fruitWithM);
}
}
Following is the output of the above program which shows the first fruit in the list that starts with "M".
First fruit starting with M: Mango
Example 4: Using Where() and Select() Together
Here, we combine the Where() and Select() methods with lambda expressions to first filter even numbers (n => n % 2 == 0) and then square them (n => n * n). This shows how multiple LINQ methods can work together with lambdas.
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
var numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
var evenSquares = numbers
.Where(n => n % 2 == 0)
.Select(n => n * n)
.ToList();
Console.WriteLine("Squares of Even Numbers:");
evenSquares.ForEach(n => Console.Write(n + " "));
}
}
Below you can see the output of the above program which shows the squares of all even numbers from the given list.
Squares of Even Numbers: 4 16 36
Common Mistakes to Avoid with Lambda Expressions in LINQ
Using lambda expressions in LINQ can sometimes lead to unexpected results, exceptions, or performance issues. Here are some common mistakes and how to avoid them.
Mistake 1: Forgetting ToList()
LINQ uses deferred execution, which means the query only runs when we actually iterate through it. If we modify the original collection before the query executes, it will reflect those changes which may not be what we want.
Example of deffered execution problem −
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 2, 4, 6, 8 };
// PROBLEM - query not executed yet
var evenNumbers = numbers.Where(n => n % 2 == 0);
// Add new element before iterating
numbers.Add(12);
Console.WriteLine("Even numbers:");
foreach (var num in evenNumbers)
{
Console.WriteLine(num);
}
}
}
In the output below, you can see that the number 12 is included even though we added it after writing the query. This happens because the query was not executed until the loop started.
Even numbers: 2 4 6 8 12
We can fix this by using ToList() to execute the query immediately.
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
// Create a list of numbers
var numbers = new List<int> { 2, 4, 6, 8 };
// Run the query immediately using ToList()
var evenNumbers = numbers.Where(n => n % 2 == 0).ToList();
// Add a new number after the query has already executed
numbers.Add(12); // This will not affect the result anymore
// Display the result
Console.WriteLine("Even numbers:");
foreach (var num in evenNumbers)
{
Console.WriteLine(num);
}
}
}
In the output below, we can see that the query result stays the same even if we modify the list later.
Even numbers: 2 4 6 8
Mistake 2: Executing a Query Multiple Times
If we call multiple LINQ methods on the same query (like Count(), First() and ToList()), the query executes each time. This can make the program slower, especially if the data set is large or the operation is heavy.
var query = products.Where(p => ExpensiveOperation(p)); int count = query.Count(); // Executes once var first = query.First(); // Executes again var list = query.ToList(); // Executes again
Here, each of these lines re-runs the same query, which is not efficient.
To fix this, we can store the result once using ToList() and then reuse it.
var results = products.Where(p => ExpensiveOperation(p)).ToList(); int count = results.Count; var first = results.First();
Mistake 3: Incorrectly Using Single() Method
A Single() method is used when we are sure that there's exactly one element matching the condition but if there are multiple matches, it will throw an exception error which can crash the program. For example -
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
static void Main()
{
var people = new List<Person>
{
new Person { Name = "Amit", Age = 25 },
new Person { Name = "Riya", Age = 30 },
new Person { Name = "Tina", Age = 15 }
};
// This will throw an exception because more than one person is aged 18 or older
var adult = people.Single(p => p.Age >= 18);
Console.WriteLine($"Adult found: {adult.Name}");
}
}
The above program will throw an exception as shown below. Here, both Amit and Riya are adults, so Single() fails because it expects exactly one match.
System.InvalidOperationException: Sequence contains more than one matching element
Here's the correct way: if we just want the first match, we should use the First() method instead of Single() method. For example −
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
static void Main()
{
var people = new List<Person>
{
new Person { Name = "Amit", Age = 25 },
new Person { Name = "Riya", Age = 30 },
new Person { Name = "Tina", Age = 15 }
};
// Use First() to get the first person aged 18 or older
var adult = people.First(p => p.Age >= 18);
Console.WriteLine($"Adult found: {adult.Name}");
}
}
And if there's a chance that no match exists, use FirstOrDefault() method to avoid exceptions.
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
static void Main()
{
var people = new List<Person>
{
new Person { Name = "Amit", Age = 25 },
new Person { Name = "Riya", Age = 30 },
new Person { Name = "Tina", Age = 15 }
};
var adult = people.FirstOrDefault(p => p.Age >= 18);
if (adult != null)
{
Console.WriteLine($"Found: {adult.Name}");
}
else
{
Console.WriteLine("No adult found!");
}
}
}
Following is the output of the above programs −
Found: Amit
Mistake 4: Modifying a Collection While Iterating
If we try to remove or add items to a collection while looping through it, it will throw an exception error. This happens because the collection is being modified while it's still being read by the loop. For example −
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
foreach (var num in numbers)
{
if (num % 2 == 0)
numbers.Remove(num); // ERROR!
}
}
}
Below in the outputof the above program where we can see that the program throws an error.
System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
We can fix this issue by using the RemoveAll() method instead. This method removes all elements that match a given condition without causing errors.
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
numbers.RemoveAll(n => n % 2 == 0); // Removes all even numbers
Console.WriteLine("Remaining numbers:");
numbers.ForEach(n => Console.Write(n + " "));
}
}
In the output below, we can see that only the odd numbers are remaining.
Remaining numbers: 1 3 5
Mistake 5: Not Handling Null Values
If a collection contains null values and we directly access their properties, it will throw a NullReferenceException error. This happens because we are trying to use an object that doesn't exist in memory. For example −
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
List<string> names = new List<string> { "Alice", null, "Bob" };
var lengths = names.Select(n => n.Length); // ERROR!
foreach (var len in lengths)
{
Console.WriteLine(len);
}
}
}
In the output below we can see that the program throws an exception error because one of the elements in the list is null and accessing n.Length is invalid.
System.NullReferenceException: Object reference not set to an instance of an object.
First way is to filter out nulls: We can first remove null values using the Where() method before accessing their properties. For example −
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
List<string> names = new List<string> { "Alice", null, "Bob" };
var lengths = names
.Where(n => n != null)
.Select(n => n.Length);
foreach (var len in lengths)
{
Console.WriteLine(len);
}
}
}
Here, we filter out null values and then calculate the string lengths. In the output below we can see the lengths of valid names.
5 3
Second way is to use the Null-Conditional Operator: We can also use the null-conditional operator (?.) with the null-coalescing operator (??) to handle nulls.
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
List<string> names = new List<string> { "Alice", null, "Bob" };
var lengths = names.Select(n => n?.Length ?? 0);
foreach (var len in lengths)
{
Console.WriteLine(len);
}
}
}
This method assigns all the null values to 0, as we can see in the below output −
5 0 3
Lambda Expression with No Parameters
When we don't need any parameters, we can use empty parentheses () in a lambda expression. It is mainly used with the Action delegate, which doesn't return any value.
Example
In this example, the lambda expression () => Console.WriteLine("Welcome to Lambda Expressions!") doesn't take any input and simply executes the Console.WriteLine() statement when called.
using System;
class Program
{
static void Main()
{
Action show = () => Console.WriteLine("Welcome to Lambda Expressions!");
show();
}
}
Following is the output of the above program.
Welcome to Lambda Expressions!
Lambda Expression with Multiple Parameters
We can pass multiple parameters to a lambda expression by placing them inside the parentheses.
Example
In this example, we use a Func delegate that takes two integer parameters (a, b) and returns their sum. We use the lambda expression (a, b) => a + b to perform the addition.
using System;
class Program
{
static void Main()
{
Func<int, int, int> add = (a, b) => a + b;
Console.WriteLine(add(14, 23));
}
}
The output below shows the sum of the given two numbers −
37
Lambda Expression with Return Keyword
If a lambda expression has more than one statement, we must use curly braces { } and the return keyword to return a value.
Example
In this example, we use the lambda expression (x, y) => { int result = x * y; return result; } to calculate the product of two numbers and use the return keyword to return the calculated result.
using System;
class Program
{
static void Main()
{
Func<int, int, int> multiply = (x, y) =>
{
int result = x * y;
return result;
};
Console.WriteLine(multiply(3, 4));
}
}
The output of this program shows the returned value after multiplying the given numbers.
12
Closures in Lambda Expressions
A closure in C# is a lambda expression that uses variables defined outside of it. These variables are called outer variables and the place where they are defined is called the outer scope.
Closures can access and remember these outer variables while the lambda runs even if the outer method has finished executing. Let's look at a few examples below of how it works.
Example 1: Capturing Single Variables
In this example, the lambda expression x => x * factor captures the outer variable factor, so calling multiply(3) multiplies 3 by 5 and returns 15 as the result.
using System;
class Program
{
static void Main()
{
int factor = 5; // variable from outer scope
// Lambda captures the external variable 'factor'
Func<int, int> multiply = x => x * factor;
Console.WriteLine(multiply(3)); // Multiply 3 by factor
}
}
Below is the output of the above program.
15
Example 2: Capturing Multiple Variables
In this example, the lambda x => x * factor + offset captures the outer variables factor and offset to calculate the result. so calling calculate(4) multiplies 4 by 2 and adds 3 and returns the result as output.
using System;
class Program
{
static void Main()
{
int factor = 2;
int offset = 3;
// Lambda captures both 'factor' and 'offset'
Func<int, int> calculate = x => x * factor + offset;
Console.WriteLine(calculate(4)); // Multiply 4 by factor and add offset
}
}
Below is the output showing the lambda using factor and offset to calculate the given expression.
11
Conclusion
In this chapter, we learned how lambda expressions in C# help us write clean and simple code by placing the function logic directly wherever it's needed. We can use lambda expressions with different types of collections, delegates, and even with event handling. By understanding it we can use it to make our C# program more clean and readable.