.NET Framework 2.0 came with the concept of Predicate Delegates. It is really a powerful concept which makes it easy to write searching algorithms on collections. These are also widely used while performing filter operations on WPF object data binding.
What is a predicate delegate?
A predicate delegate is a delegate with the following signature:
· Return type -
· Argument type - generic
So, a predicate delegate is a delegate who points to a boolean function that returns or and takes a generic type as an argument. A predicate delegate thus is a delegate which is capable of taking any custom type as an argument. This makes it quite useful because what we get as a result is a generic delegate. The function that it points to has logic to evaluate any condition and return / accordingly.
Why use a predicate delegate?
The most common use of a predicate delegate is for searching items in a collection. Because a predicate delegate is a delegate of type or generic, it is most useful when searching items in a generic collection. It really comes handy when you have a collection with tons of items and you need to perform a search operation. Using a predicate delegate not only reduces the lines of code that needs to be written, but also increases performance of the application. These are also used while performing filter operations on objects while performing data binding in WPF.
For example, create a console application and declare an class. Declare these properties for the class: , , and :
In the method of the console application, declare three employees as below:
Create a generic and put the above employees in the list:
We will demonstrate the use of a predicate delegate by performing a search operation on the .
Method #1 - The traditional way of doing it
Create a function that searches an employee first name and returns if found:
Declare a predicate delegate pointing to that function:
Call the method of the and pass the predicate delegate as an argument:
The method internally iterates through each item in the list and calls the function , passing each item from the list as an argument one by one. When the of the employee matches the condition, the function returns . The method then returns that employee object to the placeholder . That means for each item in the list, the method calls the function passing the items as arguments one by one. Whichever item satisfies the condition in the function is then returned.
Print the result to check:
This method has a disadvantage. What happens when we need to alter our search? We would need to create another search method and do the same thing as above. Actually, this method was just to explain things in an elaborate way. The better way of doing this is by using anonymous functions of C#.
Method #2 - Using anonymous functions
Consider the following code:
The functionality here is the same, but instead of declaring a separate predicate delegate and manually pointing it to an external function, we use an anonymous function. The result is much less verbose in terms of lines of code. We directly invoke the anonymous function by the delegate which takes an employee object as argument. Again, print the result to check:
Method #3 - Using a lambda expression
The lines of code can be further reduced by using => or lambda expressions. Lambda expressions are used to invoke delegates with much lesser lines of code. See the code snippet below:
The method still takes a predicate delegate. is used to invoke a delegate with an anonymous function. Here, we need to pass an argument in the anonymous function of type .
does just that. Note that intellisense already expects an object of type when typing the Lambda expression. This is because, we are performing a search operation on a generic list of type , and hence the compiler expects a delegate to a function which will pass an argument of type used for evaluating the search condition of the anonymous function. Thus we write:
Notice that our logic has been reduced to a single line of code. Finally, print the result again to check:
The method here will stop searching when it encounters the first condition. So if we have two employees with the same first name, the employee listed at the top of the list will be returned.
Searching a list of employees
To search more than one result, use the method. It is the same as the method, but returns a collection of objects meeting the search condition. The below code searches all employees with J in their first name:
Print the result:
You can also narrow down your search radius by specifying the start index and the number of items to search along with the predicate delegate that invokes the search criteria through the anonymous function:
The code searches any employee with a first name containing J from the zeroth element and spanning two counts (i.e., 0, 1 for our case).
The concepts of the other methods are the same. and do the same things, but return results from the last matches rather than the first as seen above.