:::: MENU ::::

Tuesday, April 8, 2008

Overview:

Like mathematicians, developers talk about their code in aesthetic and sometimes hygienic terms. The code is “elegant,” or it “looks clean,” and sometimes it “smells bad”. I have even heard developers refer to their code as “sexy.” Talking about your code as sexy is surely a sign that you need to get out more! Achieving elegant code is not easy and so as I deepen my experience with .Net 2.0, I am always pleased to discover when the framework offers a way to do something that I could have done in 1.1, but can now do much more elegantly. Predicates and the related Actions and Converters are just such additions to the framework. They will not revolutionize how you code, but used properly they will reduce the amount of code needed, encourage reuse, and just look sexier.

This article will examine the following questions:

  • What are Predicates?
  • How they are used?
  • How does their performance stack up against similar foreach routines?
  • What are Actions and Converters?

So What are Predicates?

A Predicate is a new class introduced by the .Net 2.0 framework. The class has the following signature: public delegate bool Predicate (T obj) and is used by collections such as List and Array to perform methods like RemoveAll, Find, FindAll, Exists, etc. Its signature reveals that a Predicate is a Delegate. What that means is that so long as the method signatures are the same –i.e. the methods have the same return type and accept the same arguments—then any method that conforms to that signature can be called in its place. C# Delegates have been compared to C or C++ function pointers, callback functions, etc. They enable you to specify what the method that you want to call looks like without having to specify, at compile time, which actual method will be called.

Also revealed by its signature is that a Predicate is a Generic method. There are a lot of good introductions to Generics so I will not traverse that ground here. Suffice it to say in this context Generic refers to the fact that the same Predicate can be used for Collections with different Types.

One slight twist with Predicates worth mentioning is that since the Predicate class is baked into the .Net framework you do not need to specify the type argument of the generic method or to create the delegate explicitly. Both of these are determined by the compiler from the method arguments you supply. You will see from the examples what this means in practice.

The chief benefit of using Predicates, besides the “coolness factor,” is that they are more expressive, require less code, promote reuse, and surprisingly, are faster than other alternatives. Using a Predicate turns out to have better performance than a similar foreach operation. This is demonstrated below.

How do I Use Them?

The best way to illustrate the use of Predicates is to compare them to foreach routines. For example, say you have a List collection like the one below. You want to find every member of that collection that starts with “a”. One reasonable approach is to use foreach and return a new List which contains only those members of the original list that start with “a”.

The List collection below will be used to illustrate the key concepts in this article.

Sample List Collection

List<string> items = new List<string>();
items.Add("Abel");
items.Add("Adam");
items.Add("Anna");
items.Add("Eve");
items.Add("James");
items.Add("Mark");
items.Add("Saul");

To filter all members of this collection that begins with "a" using forech, the method (or routine) would look something like this:

Finding All items starting with "a"

public List<string> FindWithA(List collection)
{
   List<string> foundItems = new List<string>() ;
   foreach(string s in collection)
   {
      if ( s.StartsWith("a", 
         StringComparison.InvariantCultureIgnoreCase) )
      {
         foundItems.Add(s) ;
      }
   }
   return foundItems ;
}

In this case the List collection returned would contain the following items: Abel, Adam, and Anna.

Now what if instead of retrieving every member of the collection that starts with “a” you just wanted to confirm that at least one member of the collection started with an "a"? In this case you’d likely create a new method like the one below. You might even be clever and factor out the common code to evaluate if a particular member of the collection started with “a” and use that for both the Filter and Exists methods.

Checking if an item starting With "a" exists

public bool CheckAExists(List collection)
{
    foreach (string s in items)
    {
       if (StartsWith(s)) //calls the refactored method
          return true;
    }
    return false;
}
 
// the test to see if the string starts with "a" is now factored 
// out to its own method so that both the existence check and the
// find all method could use it.
public bool StartsWith(string s)
{
   return s.StartsWith("a", 
      StringComparison.InvariantCultureIgnoreCase) ;
}

There is nothing particularly stinky about this approach and prior to Predicates, this was the best way to achieve the desired result. But let’s see how we could achieve the same ends using Predicates.

Using a Predicate to find all and check existence

public void SomeMethod()
{
   // Uses StartsWithA method to check for existence.
   if ( items.Exists(StartsWithA)) 
   {
      // Also uses the StartsWithA method, but now 
      // to find all values
      List<string> foundItems = items.FindAll(StartsWithA); 
   }
}
 
// Method conforms to the Predicate signature. 
// It returns a bool and takes a string. 
// (Note: Even though a Predicate is a generic,
// you do not need to supply the type.  The compiler 
// handles it for you.)
public bool StartsWithA(string s)
{
   return s.StartsWith("a", 
      StringComparison.InvariantCultureIgnoreCase);
}

Using Predicates just smells better --at least to my refined olfactory sensibility. The Predicate code could have also been written using an Anonymous Method. This is a good approach if the logic you are applying to the collection is not going to be used again in the application. If that logic might be reused, then my bias is to put it in a method so you don’t run the risk of code duplication. I have also found that Anonymous Methods decrease clarity as many beginning programmers do not understand the syntax. So if you do use them, make sure everyone on the team understands their use.

Using a Predicate as an Anonymous Method to find all items starting with "a"

public void SomeMethod()
{
   // FindAll now uses an Anonymous Method. 
   List<string> foundItems = items.FindAll(delegate(string s) 
      { 
         return s.StartsWith("a", 
            StringComparison.InvariantCultureIgnoreCase); 
      } ) ;
}

Sweet fancy Moses that is some elegant code! Sure the results are not different than what was achieved using a foreach loop, but using Predicates smells like it was just bathed in rosewater, swaddled, and then pampered with frankincense and myrrh. (If you are like me and wondered just what the f*#! are frankincense and myrrh, here's a link.)

Scent aside, there is a limitation to the standard implementation of Predicates. It is often the case that you will want to pass in a parameter to the method which the Predicate points to. It would be a pain to have to define a method for StartsWithA and another method for StartsWithB, etc. Because Predicates are delegates, however, you cannot change the signature to pass in additional arguments. Fortunately, it is easy enough to wrap the predicate in another class so you have access to additional parameters in your predicate method. There is a good article by Alex Perepletov entitled “Passing parameters to predicates” demonstrating this technique.

Of course, I've only demonstrated a few of the methods in the .Net framework that utilize predicates. I encourage you to review the API for Array and List to view the other methods of those classes that use Predicates. (I am not sure if there are any other classes that have methods which use Predicates, so if there are any outside of Array and List, please let me know so that I can post them too.)

Performance

I would argue that even if the Predicate class is a little slower than similar looping structures like foreach or for they are still preferable as Predicates have other more important virtues. I know there are performance Nazis out there who agonize over nanosecond differences, but if a nanosecond difference is that important to your application than it is likely you should not be using C# at all. Still having an understanding of the performance impact of your programming decisions is a good thing and I was curious about how Predicates stacked up so I put together a simple head-to-head comparison of Predicate vs. foreach. The test compared a List of 100,000 string values to see which ones started with "1." For each test iteration, the search was performed 100 times. The results are below.

C# Predicate Performance

As you can see, Predicates are the winner. We are, however, talking milliseconds and, in most situations, nanoseconds so I would not take one approach or the other on performance considerations alone.

Also a few days after I completed these tests, I found another article that conducts a more through comparison between Predicates and foreach. You can find that comparison at Jon Skeet's Coding Blog.

As Long as We're Here: Actions and Converters

In addition to the Predicate class, .Net 2.0 also introduces the Action and Converter classes. Like Predicates, these are generic delegates. The Action class provides a simple way to walk all items of a collection and call a method on each member. Both Action and Converter work in the same way as the Predicate class --including the ability to either define or use Anonymous methods. For example, if you wanted to display all the members of a List collection, you could use the following:

Using an Action to display all members of a collection

public void SomeMethod()
{
   // I am using an Anonymous Method. As with Predicates,
   // I could have also defined a method and used it.
   items.ForEach(delegate(string s)
   {
      Console.WriteLine("value: {0}", s);
   });
}

The Converter class is used to change all members of a collection from one type to another type. The signature of the Converter class is a little different than the Predicate or Action classes: public List ConvertAll (Converter converter). It is, however, used basically in the same way. For example, if I wanted to turn a List collection of strings into a list collection of Names, (assuming for the moment that I had created a Name class,) I could do the following.

Using a Converter to convert a List of strings.

public void SomeMethod()
{
   // The Converter must specify the type to be converted, string, 
   // the type it is being converted to, Name, and the method
   // doing the conversion, StringToNameConverter.
   List<Name> names = 
      items.ConvertAll(new Converter<string, Name>( StringToNameConverter));
}
 
// The method used by the ConvertAll method to do the actual
// conversion of strings to Names.
private Name StringToNameConverter(string s)
{
   return new Name(s);
}
 
// Our Name class might be defined as follows:
public class Name
{
   private string m_value;
   public string Value
   {
      get { return m_value; }
      set { m_value = value; }
   }
 
   public Name(string name)
   {
      Value = name;
   }
}

I did not benchmark the Action or Converter class, but my hunch is that they too would offer slight performance benefits over similar routines using foreach. Like the Predicate class, the chief benefit they offer is that they make it easier to write the same elegant, sweet smelling code and will likely encourage reuse as well. Finally, if you need to pass parameters to either class, you can wrap them in another class as shown in the Predicate example cited earlier.

Conclusion:

So we have arrived at the end of the article. Likely at this point you are so seduced by the sexiness of the code that the Predicate, Action, and Converter classes make possible that you are contemplating leaving your significant other and moving to Massachusetts to marry your application --which is, I believe, legal here in Boston. I wish you both well in that endeavor!

As always, do not hesitate to email me if you have any questions (my email is in the "About the Author" tab above.) If you extend what I've done or have additional information I missed, I would also greatly appreciate your letting me know. (Leaving a comment, positive or incredibly positive, is also always appreciated.)

 

Categories: