Pred is a whaticate? Generics on Steroids - Part I of II
I mean: What is a predicate?
To be honest, at the beginning I got tired of all of the hype of .NET Generics. Yes, they are pretty neat but I could never see their use outside of creating strongly typed collections with a few lines of code.
Was I wrong.
So, I set out to prove to people some of the sweet things that .NET Generics can do for us programmers; to be honest, the possibilities are limitless when one takes reflection and anonymous methods into the picture.
Let's see what .NET Generics can do in the real life of a programmer by starting with this class:
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
namespace ExcellenceInSoftware.Examples.Generics.EmployeeServices
{
public class Employee
{
private int _Id;
private string _FirstName = String.Empty;
private string _MiddleInitial = String.Empty;
private string _LastName = String.Empty;
private string _Department = String.Empty;
[XmlAttribute]
public int Id
{
get
{
return _Id;
}
set
{
_Id = value;
}
}
[XmlAttribute]
public string FirstName
{
get
{
return _FirstName;
}
set
{
_FirstName = value;
}
}
[XmlAttribute]
public string MiddleInitial
{
get
{
return _MiddleInitial;
}
set
{
_MiddleInitial = value;
}
}
[XmlAttribute]
public string LastName
{
get
{
return _LastName;
}
set
{
_LastName = value;
}
}
[XmlAttribute]
public string Department
{
get
{
return _Department;
}
set
{
_Department = value;
}
}
}
}
So, that is just a simple class that contains information about a virtual Employee. I have prepared the class for Xml Serialization because I plan to populate the data from an xml file. So, the [XmlAttribute] class is really a side note here.
Now, back in the day before .NET 2.0 if one wanted to have a custom collection of Employees that might create a class called EmployeeCollection that inherited from a System.Collections.ArrayList. The downside to this method was that the collection was not strongly typed and therefore, the possibilities for more runtime errors exist. However, in .NET 2.0 generics solved this problem. Here is my EmployeeCollection class that inherits from a Generic List:
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ExcellenceInSoftware.Examples.Generics.EmployeeServices
{
[Serializable]
[XmlRoot("Employees")]
public class EmployeeCollection : System.Collections.Generic.List<Employee>
{
}
}
Now, again, ignore the Xml Serialization attributes because they have nothing to do with these examples. What we have here is a simple class that inherits from System.Collections.Generic.List using the type (<T>) of Employee. Thus, all methods contained within the EmployeeCollection class and its base classes will automatically require me to pass an Employee object to them in order to be successfully compiled. But that is not the point of this highly instructionable post. Take a look at the data I am going to populate my EmployeeCollection with:
<?xml version="1.0" encoding="utf-8"?>
<Employees>
<Employee Id="2002" FirstName="Jonathan" MiddleInitial="C" LastName="Stewart" Department="Accounting" />
<Employee Id="2003" FirstName="Michael" MiddleInitial="X" LastName="Thadson" Department="Accounting" />
<Employee Id="2004" FirstName="Habib" MiddleInitial="M" LastName="Sedieri" Department="Accounting" />
<Employee Id="2005" FirstName="Michelle" MiddleInitial="B" LastName="Lawson" Department="Human Resources" />
<Employee Id="2006" FirstName="Tyra" MiddleInitial="K" LastName="Whitaker" Department="Human Resources" />
<Employee Id="2007" FirstName="Bethany" MiddleInitial="F" LastName="Moore" Department="Risk Management" />
<Employee Id="2008" FirstName="Jonathan" MiddleInitial="C" LastName="Stewart" Department="Risk Management" />
<Employee Id="2009" FirstName="Ponce" MiddleInitial="C" LastName="DeLeon" Department="Legal" />
</Employees>
Simple data used for an example. Let's take a look at some of the 'real-life' tasks that we might be required to perform in a production environment. One of our virtual requirements is to find all Employees that are in the Accounting department after the EmployeeCollection has been populated with this data above. In .NET 1.1 we could have easily created another EmployeeCollection and added Employee objects to it while looping through a for-each statement, but that was always a tad tedious and actually consumed more memory than I'd like it to.
Enter .NET 2.0 Predicates and Actions.
If you take a look at the documentation for a System.Collections.Generic.List it has 2 methods that I want to demonstrate here - FindAll and ForEach.
The FindAll method takes a .NET 2.0 Predicate as a parameter and the ForEach method takes a .NET 2.0 Action as a parameter.
A .NET 2.0 Predicate is pretty much the same thing as a delegate but it's signature is a little bit different. A Predicate is basically a pointer to a method that will perform a task on each item within the collection. So, let's declare our Predicate:
First, we need the method that the Predicate will point to -
private bool IsInAccountingDepartment(Employee e)
{
return (e.Department == "Accounting");
}
All this method does is check to see if the respective Employee is in the Accounting department and returns 'true' if so. Now, we need to declare the instance of the predicate that points to the above method:
private Predicate<Employee> accounting = new Predicate<Employee>(IsInAccountingDepartment);
Here, we create a new Predicate of type Employee called accounting. Then, within the constructor of the Predicate, we point it to the method created above called 'IsInAccountingDepartment'. Therefore, any method that references the 'accounting' predicate will automatically be pointed to the 'IsInAccountDepartment' method. Let's take a look at the implementation:
[Test]
public void FindTotalAccountingEmployees()
{
EmployeeCollection employees = FromXml();
List<Employee> accountingEmployees = employees.FindAll(accounting);
Console.WriteLine("Total Accounting Employees: " + accountingEmployees.Count.ToString());
}
In this method FindTotalAccountingEmployees I first populate my EmployeeCollection using a method called FromXml() that basically reads the xml file above into the collection of Employee objects. Then, I create an instance of the 'List' generic called accountingEmployees that is populate from the FindAll method of the EmployeeCollection. Notice how I passed in our Predicate instance called 'accounting' into the FindAll method.
Amazing as it is, it returns 3 employees which is the correct number of Employees in the accounting department per the Xml file shown previously in this document.
So, what is happening here? Basically, when the FindAll method is called on the EmployeeCollection object using the accounting Predicate, each Employee contained with the EmployeeCollection is sent to the 'IsInAccountingDepartment' method and if that method returns true, the respective Employee object is added to the accountingEmployees list. Therefore, the accountingEmployees list only contains Employees who are in the Accounting department.
A real-life scenario that doesn't use foobars. Makes sense to me.
Tomorrow, I will demonstrate what a .NET 2.0 Action is and how to promote all Employees within an EmployeeCollection to the same department. Until then...
stop programming and start developing the business.