Updated on 06 Oct 2025 by Admin

Lambda Expression, Func, Predicate and Action

Func, Predicate, and Action are nothing but ready-made delegates, whereas lambda expressions are a shorthand way of defining inline delegates. I know, at this moment, you don't understand anything. But don't worry. It is because you don't know what a delegate is yet.

What is a Delegate?

Like string is a type, class is a user-defined type, delegates are also a type in C# that act as a function pointer, meaning that it can point to any method of the same signature and, as it is a type so it can be passed as a parameter or an argument to a function as well.

Please go through the following example:

public class Program
{
public  delegate int calculate(int x, int y); //Declaring a delegate
public static void Main()    
{
calculate c1 = Add;  //Delegate pointing to the Add() method
calculate c2 = Multiply; //The same delegate pointing to the Multiply() method
Console.WriteLine(c1.Invoke(2,4)); //It will add two numbers
Console.WriteLine(c2.Invoke(2, 4)); //It will multiply numbers
Console.ReadLine();
}
public static int Add(int x, int y)
{
return x + y;
}
public static int Multiply(int x, int y)
{
return x * y;
}
}
}

  • As you can see, we have declared a delegate named calculate that takes two parameters of type int and returns an integer.
  • As a delegate can point to any method of the same signature, we created two methods: the first method adds two numbers, while the second method multiplies two numbers.
  • Then, in the Main() method, you can notice that the same delegate is pointing to both the Add() and the Multiply() methods.
  • To invoke a delegate, we use the Invoke() method.
  • So, the concept is pretty simple; we can create a delegate that can point to any method of the same signature.

I have already mentioned that delegates are types in C#. Since delegates are types and could be passed as an argument to a method, it becomes very powerful and gives dramatic flexibility to your code. Now, you can construct a method where the consumer of the method could define a part of the method's logic. Isn't it amazing that you can define a method that will have partially defined logic in it, and the rest of the logic will be given by the caller who will consume your method?

In short, with delegates, you can define a partially defined method where a part of the method's logic is defined by the caller who will consume it.

Let's understand this with an example. Suppose you have to define a method to return a list of employees based on these three conditions:

  1. Return all employees where gender is male. (Return list of all male employees
  2. Return all employees whose salary is above $2000
  3. Return all employees whose name starts with 's'

But there is one more constraint that the condition will be defined by the caller who will call the method, so basically, you have to return employees based on an unknown condition.

This can easily be achieved with delegates. To define a delegate, we use the keyword delegate and the rest is similar to a method declaration.

public class Employee
{
public delegate bool ShouldReturn(Employee emp);
public int Id { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
public int Salary { get; set; }
public IEnumerable<Employee>GetEmployee(List<Employee> emps, ShouldReturn condition)
{
foreach (Employee emp in emps)
{
if (condition(emp))
{
yield return emp;
}
}
}
}

  • As you can see, the Employee class contains a delegate that accepts an Employee object and returns a bool.
  • The GetEmployee() method accepts a list of Employees and a delegate, based on which we are returning an employee.
  • Now the caller will define the condition, and he is free to define any condition he wants, as a delegate can point to any method of the same signature, so the caller needs to create a method that should return a 'bool'.

static void Main(string[] args)
{
List<Employee> emps = new List<Employee>() {
new Employee(){Id=1,Name="Sachin",Gender="Male",Salary=2000},
new Employee(){Id=1,Name="Arjun",Gender="Male",Salary=1000},
new Employee(){Id=1,Name="Vikash",Gender="Male",Salary=3000},
new Employee(){Id=1,Name="Nivi",Gender="Female",Salary=5000},
};
Employee employee = new Employee();
var res1 = employee.GetEmployee(emps, MyCondition1); //Passing condition1
foreach(var emp in res1)
{
Console.WriteLine(emp.Name);
Console.WriteLine(emp.Gender);
Console.WriteLine(emp.Salary);
Console.WriteLine("----------");
}
var res2 = employee.GetEmployee(emps, MyCondition2); //Passing condition2
foreach(var emp in res2)
{
Console.WriteLine(emp.Name);
Console.WriteLine(emp.Gender);
Console.WriteLine(emp.Salary);
Console.WriteLine("----------");
}
var res3 = employee.GetEmployee(emps, MyCondition3); //Passing condition3
foreach(var emp in res3)
{
Console.WriteLine(emp.Name);
Console.WriteLine(emp.Gender);
Console.WriteLine(emp.Salary);
Console.WriteLine("----------");
}
Console.ReadLine();
}
public static bool MyCondition1(Employee emp)
{
if (emp.Name.StartsWith("S"))
{
return true;
}
return false;
}
public static bool MyCondition2(Employee emp)
{
if (emp.Salary>2000)
{
return true;
}
return false;
}
public static bool MyCondition3(Employee emp)
{
if (emp.Gender=="Female")
{
return true;
}
return false;
}
}
}

In the above example, the Main() method of the class Program is the caller, so we created three different conditions (method of the same signature as that of the delegate), and now we are free to pass any condition we want as a parameter to the GetEmployee() method.

So, with delegates, we can delegate the logic to the caller, where the caller can define the logic as per its requirement.

Anonymous Method

We know, a delegate can point to any method of the same signature, as shown below:

public delegate int Calculate(int x,int y);
public int Add(int x, int y)
{
return x+y;
}
public int Multiply(int x, int y)
{
return x*y;
}
Calculate c= add;
Calculate c1=Multiply;

Notice, the delegate Calculate is pointing to two different methods, Add and Multiply. This is good, but it would be better if somehow we could define them inline instead of creating two external methods, and this can be achieved with the Anonymous method as shown below:

Calculate c= delegate(int x,int y )
{
return x+y;
} 

Notice, we have not created any external methods; rather, the delegate Calculate is pointing to different methods inline.

So, the takeaway is that if your delegate is pointing to a method that has a few lines of code, then you can use an anonymous method, which is called an anonymous method because it is exactly a method just without a name.

Lambda Expressions

We know that to define an inline delegate, we can use Anonymous methods. With the anonymous method, we do not need to create an external method, which saves some time.

Lambda expressions take this concept one step further, and make the delegates even simpler as shown below:

 Calculate c= (x,y)=>x+y;

So, a lambda expression is a way to define an inline delegate. In a lambda expression, the left side of the arrow becomes the arguments of the method, while the right side of the arrow acts as the method body.

Let's understand this with more examples:

public class Program
{
public delegate void display();
public void Print()
{
Consolw.WriteLine("My name is Sachin.");
}
}
display d=Print();
d.Invoke();

This can be rewritten with the help of a lambda expression as follows:

public class Program
{
public delegate void display();
display d=()=>Console.WriteLine("my name is sachin"); // no argument just body
d.Invoke();

Delegates
Different ways of writing delegates

With the example of the Employee class, we have seen that a delegate can be used to delegate the method's logic to the caller, and the caller can define the logic as per its requirement.

There, we followed the normal approach of defining delegates; the same could be achieved with lambda expressions, as shown below:

static void Main(string[] args)
{
 List<Employee> emps = new List<Employee>() {
new Employee(){Id=1,Name="Sachin",Gender="Male",Salary=2000},
new Employee(){Id=1,Name="Arjun",Gender="Male",Salary=1000},
new Employee(){Id=1,Name="Vikash",Gender="Male",Salary=3000},
new Employee(){Id=1,Name="Nivi",Gender="Female",Salary=5000},           
};
Employee employee = new Employee();
var res1 = employee.GetEmployee(emps,x=>x.Name.StartsWith("s")); //Passing condition1
foreach(var emp in res1)
{
Console.WriteLine(emp.Name);
Console.WriteLine(emp.Gender);
Console.WriteLine(emp.Salary);
Console.WriteLine("----------");
}
var res2 = employee.GetEmployee(emps,x=>x.Salary>2000); //Passing condition2
foreach(var emp in res2)
{
Console.WriteLine(emp.Name);
Console.WriteLine(emp.Gender);
Console.WriteLine(emp.Salary);
Console.WriteLine("----------");
}
var res3 = employee.GetEmployee(emps, x=>x.Gender=="Female"); //Passing condition3
foreach(var emp in res2)
{
Console.WriteLine(emp.Name);
Console.WriteLine(emp.Gender);
Console.WriteLine(emp.Salary);
Console.WriteLine("----------");
}          
Console.ReadLine();           
}      
}
}

Please note that in the above example, the caller (class program's Main() method) has not created any external method; instead, it passed a lambda expression as an argument to the GetEmployee() method.

Func , Predicate and Action

Func, Action and Predicate were first introduced in C# 3.0 and they are referred as Generic Delegates.

Func: Func can take any type of input and can return any type of output.

Func<double,double> area= x=>x*x;
Func<int,double> area= x=>3.14*x*x;

Predicate: Predicate can take any type of input, but it can only return a bool output.

predicate<string> x=> x.Length>5;
predicate<int> x=>x%2==0;

Action: Action can take any type of input, but can return void output only, which means it simply doesn't return anything. It just takes input and processes it as per the definition.

Action<string> res=()=>Console.WriteLine("My name is sachin");

We now understand that a delegate is a type and can be used as an argument to a method, and the consumer of the method is responsible for defining the delegate. So, Func, Predicate, or Action can be used as parameters inside a method, and the consumer of the method will be responsible for defining them, so let's modify our Employee class to use ready-made delegates.

public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
public int Salary { get; set; }
public IEnumerable<Employee> GetEmployee(List<Employee> emps, Predicate<T> condition)
{
foreach (Employee emp in emps)
{
if(condition(emp))
{
yield return emp;
}
}
}
}

Notice that we have changed the method such that it takes a predicate instead of the user-defined delegate. So, now the consumer of the method can easily provide a lambda expression as an argument to the method.

static void Main(string[] args)
{
List<Employee> emps = new List<Employee> () {
new Employee(){Id=1,Name="Sachin",Gender="Male",Salary=2000},
new Employee(){Id=1,Name="Arjun",Gender="Male",Salary=1000},
new Employee(){Id=1,Name="Vikash",Gender="Male",Salary=3000},
new Employee(){Id=1,Name="Nivi",Gender="Female",Salary=5000},           
};
Employee employee = new Employee();
var res1 = employee.GetEmployee(emps,x=>x.Name.StartsWith("s")); //Passing condition1
foreach(var emp in res1)
{
Console.WriteLine(emp.Name);
Console.WriteLine(emp.Gender);
Console.WriteLine(emp.Salary);
Console.WriteLine("----------");
}
var res2 = employee.GetEmployee(emps,x=>x.Salary>2000); //Passing condition2
foreach(var emp in res2)
{
Console.WriteLine(emp.Name);
Console.WriteLine(emp.Gender);
Console.WriteLine(emp.Salary);
Console.WriteLine("----------");
}
var res3 = employee.GetEmployee(emps, x=>x.Gender=="Female"); //Passing condition3
foreach(var emp in res2)
{
Console.WriteLine(emp.Name);
Console.WriteLine(emp.Gender);
Console.WriteLine(emp.Salary);
Console.WriteLine("----------");
}           
Console.ReadLine();          
}            
}
}

The knowledge of delegates and lambda expressions becomes necessary when you are working with LINQ standard extension methods, which is why I have explained extension methods, delegates, and lambda expressions before dealing with LINQ standard query operators.

For example, LINQ where() is an extension method on IEnumerable<T> which accepts a predicate delegate (a Func of return type bool is equivalent to a predicate delegate) that is the reason we are able to pass a lambda expression as an argument to it

List<int> numbers= new List<int>{1,2,3,4,5,6,7,8,9,10};
var res = numbers.where(x=>x%2==0);
// is equivalent (same as) to 
var res = numbers.where(checkEven);
public bool checkEven(int x)
{
if(x%2==0)
{
return true;
}
else
{
return false;
}


Sharpen Your Skills with These Next Guides