Func, Predicate and Action are nothing but readymade delegates , where as lambda expressions are shorthand way of defining inline delegates. I know, at this moment, you din't understand anything , don't worry, it is because, you don't know what is a delegate, yet.
what is a delegate?
Like string is a type , class is a user defined type , Delegates are also a type in c# which acts as a function pointer , means it can point to any method of 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 below example
public class Program
{
public delegate int calculate(int x, int y); // declare a delegate
public static void Main()
{
calculate c1 = Add; // delegate pointing to Add method
calculate c2 = Multiply; // same delegate pointing to 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 which takes two parameters of type int and return an integer.
• As a delegate can point to any method of the same signature, we created two methods, the first method does the addition of two numbers while the second multiply two numbers.
• Then in the Main method, you can notice that the same delegate is pointing to both Add method as well as the Multiply method.
• 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 a part of the method logic could be defined by the consumer of the method, isn't it amazing that you can define a method which 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 it with an example, suppose you have to define a method to return a list of employees based on these three conditions
1. Return all employee where gender is male. (Return list of all male employees)
2. Return all employee whose salary is above 2000 $
3. Return all employee 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 the delegate we are returning 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 class program is the caller , so he created three different conditions (method of same signature as that of the delegate) and now he is free to pass any condition he wants as parameter to GetEmployee() method.
So , with delegate we can delegate the logic to the caller , where the caller can define the logic as per his requirement.
Anonymous Method
We know , a delegate can point to any method of 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 anyhow we could define them inline instead of creating two external methods, and this can be achieved with 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 take away is , if your delegate is pointing to a method which has a few lines of code then you can use anonymous method , it is called as anonymous method because it is exactly a method just without a name.
Lambda Expression
We know that in order to define an inline delegate we can use Anonymous methods , with anonymous method we do not need to create an external method which saves some time.
Lambda expression takes this concept to one step further, and makes the delegate even more 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 Left side of the arrow becomes the arguments of method while the right side of arrow act 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 lambda expression as
public class Program
{
public delegate void display();
display d=()=>Console.WriteLine("my name is sachin"); // no argument just body
d.Invoke();
with the example of Employee class we have seen, that delegate can be used to delegate the method's logic to the caller, and the caller can define the logic as per his 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 , in the above example the caller (class program's Main() method) has not created any external method instead he passed a lambda expression as 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 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, 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 understood 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 readymade 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 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 , that is why i have explained Extension method , delegate and lambda expression before dealing 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 lambda expression as 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;
}