25.2.08

Using Predicate<T> in Your Methods

If you ever use any of the generic List<> functions like Find, RemoveAll, et cetera, you should be familiar with Predicate<T>. In short, it is a delegate that returns true or false, based on whatever criteria is needed. So if you are looking through a List<int> to find an element that is evenly divisible by 7, you can do it with a foreach (or for) loop:
List myList;
int x = -1;
foreach (int i in myList)
{
if (i % 7 == 0)
{
x = i;
}
}
or by using a predicate:
int x = myList.Find(i => i % 7);

Since my example uses a value type, you have to watch out for confusing no match with an actual match. In the foreach, x is initialized to -1. If -1 is a possible match (it isn't, in this case, since -1 % 7 = -1), you won't be able to determine whether or not it worked. You can always add a boolean flag, separate from the int value.

With the Find() method, it returns default(T) if there is no match. For value types, default is 0, so if that is a possibility, you will have problems. There is a FindIndex() method which would work more appropriately in this case.

If you are using reference types, default is null--so you don't have to worry!

Now that I've illustrated Predicates, let's look at how you can use them in your code.
public T FindSomethingOrOther(Predicate<T> match)
This function will find something or other, either in a static property somewhere, or within the instance it exists--either way you want.

To use the predicate, all you have to do is use it like a function:
public T FindSomethingOrOther(Predicate<T> match)
{
foreach (T obj in this.Objects)
{
if (match(obj))
return obj;
}
return null;
}

This is a powerful way to write utility functions that are not limited to one use. The above example is very basic--not really the best example. However, if the method wasn't iterating through a local collection, but through rows in a database, there would be plenty of setup and teardown code. Instead of writing a separate method to match on each column, you could write one method that took a delegate(DataRow).

No comments: