To have a clear undestanding of Predicates, you must have a good understanding of delegates.
So first things first, what is a delegate?
A delegate is a data type that can be used to hold references to methods.
A data type defines a structure for the objects that are created based on it. For example, when you create an object based on a class, that object has the structure of the based class. To create such classes we use the "class" (or struct or enum) keyword. As I mentioned in the beginning, a delegate keeps references to methods. So to define the structure of methods that can be added to an instance of a delegate we create a new type using the "delegate" keyword.
The structure of a method is defined based on its signature and the return type. The signature consists of two main sections, the name of the method and parameter information. When defining a delegate we will only take the parameter information portion of the method signature into account along with the return type. Let's have a look at a sample delegate.
public delegate int AddDelegate(int a, int b);
The delegate mentioned above specifies the structure for a method reference holder, saying that it can hold references to methods that take two integer parameters and return an integer value.
Since we are going to need methods to be referred to by the delegate, let's create some.
public int Add(int a, int b)
{
return a + b;
}
public int Sum(int a, int b)
{
return a + b;
}
There are a few ways you can add method references to a delegate, here are some.
AddDelegate addDelegate = new AddDelegate(Add);
addDelegate += Sum;
Just invoke the delegate instance with parameters (such as "addDelegate(10,5)") to call the methods associated with the delegate.
Ok, with that theory part in mind, let's have a look at what a "predicate" really is. A predicate is a delegate that can take generic type parameters, and it will always return a Boolean value. So in other words, a predicate is a predefined generic delegate in the .Net framework that can hold method references for methods that returns a Boolean value. Let's have a look at an example.
Say, I have an employee class and a collection of employees.
class Employee
{
public string Name { get; set; }
public int Age { get; set; }
}
var employees = new List<Employee>{
new Employee{Age=27,Name="Dhanushka"},
new Employee{Age=34,Name="Chanaka"},
new Employee{Age=26,Name="Dilrukshi"}
};
I am going to perform a find on this collection, first I am going to find by the age comparing against a specific age value. Then I'm going to do the same using a name just to prove my argument. So since I am using a predicate (or a delegate) I need to create methods to add into it.
public static bool FindByAge(Employee employee)
{
return employee.Age > 30;
}
public static bool FindByName(Employee employee)
{
return employee.Name == "Chanaka";
}
Since we have the methods that do the actual finding let's bind them to the predicate to use it against a collection of employees.
var findByAgePredicate = new Predicate<Employee>(FindByAge);
var findByNamePredicate = new Predicate<Employee>(FindByName);
Let's pass them to the Find() method:
//using predicates
Console.WriteLine("Predicates\n");
var age01 = employees.Find(findByAgePredicate);
var name01 = employees.Find(findByNamePredicate);
Console.WriteLine("By Age : {1}-{0}", age01.Age, age01.Name);
Console.WriteLine("By Name : {1}-{0}", name01.Age, name01.Name);
This will pass each employee as an argument to the assigned method's parameter via the Predicate. And will return a Boolean value based on the match defined in the method.
Ok, let's do the same thing using an anonymous method.
//using anonymous methods
Console.WriteLine("\nAnnonymous Methods\n");
var age02 = employees.Find(delegate(Employee employee)
{
return employee.Age > 30;
});
var name02 = employees.Find(delegate(Employee employee)
{
return employee.Name == "Chanaka";
});
Now instead of writing a method and assigning it to a Predicate you are just directly passing the method into a predicate parameter. Which accepts a delegate that returns a Boolean value as the parameter type. If you are familiar with jQuery you will find some similarity in the syntax:
$("#btnId").click(function(){logic});
Now let's take this another step further and use a lambda expression to get this done.
//using lambda expressions
Console.WriteLine("\nLamda Expressions\n");
var age03 = employees.Find(x => x.Age > 30);
var name03 = employees.Find(x => x.Name == "Chanaka");
Console.WriteLine("By Age : {1}-{0}", age03.Age, age03.Name);
Console.WriteLine("By Name : {1}-{0}", name03.Age, name03.Name);
Here you don't have to go through all the trouble you went through earlier, just specify a lambda expression; that is, an expression that returns a method. Here the first lambda expression is read as: "Given x see if x.Age is greater than 30, and return the comparison output" so this is again just a method that accepts a parameter "x" that is of "Employee" type and returns a Boolean value. Which satisfies the structure of a Predicate.
So finally all I must emphasize is that all of the mechanisms mentioned above, whether it is a predicate, anonymous method or lambda expressions, all of them are just delegates or references to methods. And my advice is to use lambda expressions for these kinds of scenarios, since it is more concise and easy to understand and all the logic is in a single place.
I hope I have clarified this issue for you, because I find this as a topic most of the developers get confused in, if you have any questions just drop a comment below.