Predicates in Action
Generics offer tremendous flexibility – in more ways than you would ever know. Predicates are a good example for this. .NET 2.0 BCL comes with certain generic delegates – Action<T>, Predicate<T>, and Converter<Tin, Tout> respectively. Their definitions are given below:
public delegate TOutput Converter<TInput, TOutput>(TInput input);
public delegate void Action<T>(T obj);
public delegate bool Predicate<T>(T obj);
If you look at the definitions, you can guess that the instances of these delegates hold references to methods that do type specific operations. For example, the Predicate delegate represents a method which takes an instance as parameter, and determines if the instance meets a particular criteria. Quite obviously, these delegates would be most applicable to generic collections and arrays. The List<T> class for instance, employs them in Find, FindAll, Exists, FindIndex, FindLastIndex, ForEach methods. You could find similar usages in the Array class.
The real cool thing is using predicates in concert with anonymous methods. At first, it may look convoluted, but actually, that makes coding real easy (and fun). The oversimplified code snippet given below does a couple of things – given a list of accounts, it finds all accounts with a zero balance, and for each of these accounts sends a mail notifying the same. The FindAll method uses a Predicate<T> delegate and the ForEach delegate employs the Action<T> delegate.
List<Account> accounts = GetAllAccounts();
accounts.FindAll(
// Uses the Predicate<T> delegate
delegate(Account account)
{
return (account.Balance == 0);
}
).ForEach
(
//Uses the Action<T> delegate
delegate(Account account)
{
SendMail(account, "Be aware, you now have a zero balance");
}
);
What you may have noticed is that we have completed avoided nested for/foreach loops here. I have used anonymous methods inline here, but you can easily replace them with delegate instances so as to accommodate different criteria or action items at runtime. In more advanced cases, you could generate predicates at runtime based on user provided business rules or something like that.
Thus is the amount of flexibility could can reap out of generics! I would strongly recommend one to go over this interesting MSDN Magazine article on Predicates by Ken Getz to get a better idea.