Since .NET 2.0 generics have been available to .NET developers. I have worked with developers that have been excited to use the new features of Generics but have been hindered by the concept of predicates. I hope to expose the simplicity and elegance of Predicates so that they are better understood and added as a valuable tool in your developer tool belt.
For simplicity I will pose the most common questions I have received from developers and show how these problems can be solved with a Predicate.
I have a List<> of strings and I just want to see if a particular string already exists in the list. I used to be able to simply pass in an object to the Exists() method of an array list to check its existence, now it asks for a predicate.
One option is to create a class that inherits from List<string> and implements an override for the Exists() method that takes a string. Here I will show you how to do this with a predicate.
The FindStringPredicate class will allow you to specify what string to find and how to find it. Now you are probably looking at this right about now and saying, “How is this better than simply passing in a string to an ArrayList.Exists() method?” Well in this case it is a good deal of overhead, BUT when you are writing large programs using lots of modeled classes you are not dealing with a lot of primitive types but rather you are dealing with complex objects. The good part is that you can create a whole library of primitive type predicate objects that you can use in any project.
With that said, you now need to actually find a string in the string list.
The output of this code is the following:
Does Avicenna exist? True
So what is happening here? Well we added a few names to a generic List<> of type string. We then created an instance of the FindStringPredicate class and passed in the name we were looking for and also told the FindStringPredicate class to do a case sensitive comparison. Behind the scenes the generic List<>.Exists() method called the FindStringPredicate.Find() method for each item in the list. The logic in the Find() method then compared the item in the list to the value of the string set in the FindStringPredicate class.
What if I want a quick and dirty way of checking if a string already exists, is there an easier way?
Yes! If your logic is simple and you do not plan on reusing it elsewhere you can create an anonymous delegate to perform your test. This sounds complicated but basically all you are doing is defining a piece of code to have the Exists() method call for each item in the list. This is how you do it:
In this code block you can see that instead of passing a target method to the constructor of the Predicate<> class we are simply passing in an anonymous delegate. This anonymous delegate consists of an argument of string lineItem. This is because the Exists() method calls your predicate for each item in the list. When it calls the predicate it passes in an object of the type you specified in the new Predicate<string> section of code. So in this case the Exists code passes in strings as the argument to the predicate’s target method. Now enclosed in the parenthesis you have a line of code that returns a Boolean value. This block of code can be as large or small as needed.
On to the Real World usage of Predicates
While the first example is a little drawn out it is really not addressing the true value of Predicates. The real value comes in to play when you have complex classes in a generic list. Because there are generic predicates you can specify the predicate’s target argument type (aka the type of the object that exists in the generic list.)
In this example we will have a generic list of Car objects. We want to find all the cars that have green paint.
First we will define our car class as follows:
Now we will define a class that will allow us to query a generic list of Car classes.
This class takes a Car object as a parameter and sets a public field to the value of the Car class. The idea here is that we can use a simple car object instance to define our query. You could have also taken in an argument of type CarColor but we want to use this CarPredicate class for more than just color based queries. There must be a dozen ways you can do this but I think this approach sets you up for understanding LINQ in the near future.
So with this class we have defined a method FindByColor which will be used to compare a car object against the CarQuery instance field. If the color matches it returns true otherwise it returns false.
Now we will use our CarPredicate class to search for all the green cars in a generic list of Car objects:
The result of this code is the following:
Car Make: Acura, Model: Integra, Color: Green
Car Make: Jeep, Model: Liberty, Color: Green
The code builds a list of Car objects with various colors, makes and models. Next we create an instance of the CarPredicate class and pass in a partially populated car object. Next we call the FindAll() method of the generic list and pass in the FindByColor method as the target of the predicate. Notice that we are creating an instance of a generic predicate with the code: new Predicate<Car>. This means that when the target method, in this case FindByColor, is called an argument of type Car will be passed to the FindByColor method.
Lets extend the CarPredicate class a bit to allow you to query for more than just color.
Add a new color of “Any” to the CarColor enum.
Now we have a Find method in the CarPredicate class that will allow us to search for cars by name, make or model simply by specifying any combination of properties of the CarQuery class.
We can use this new method in the following manner:
This code segment finds the Jeep in the list and outputs the values of the class.
I hope now that you see the value of these new Predicate based methods of the Generic list. Not only do you have the power of defining your search code and reusing the search code anywhere you use collections of your objects but you have the power of strongly typed lists.