The best way expressing a simple criteria: Predicate Generic Delegate

There are alot of classes and features that the .Net framework provides. It is so easy to get overwhelmed by them and missing out on some useful little classes. I think System.Predicate or Predicate Generic Delegate. is one of them.



There are many posts and tutorial about this predicates, but (from the brief search that I did) it does not contain as enough information as I want. So aside from defining what predicate is, I'm going to try and give a different and more complete example. For one, I'm not going to use ArrayList for any of the example (-:

So this post will discuss when to use Predicate and how to 'fully' use it. That is from declaring predicate delete to using the predicate delegate.

What is Predicate?

Predicate delegate was first introduced in .Net 2.0. It represent a method that defines a set of criteria and determine whether the specified object meets the criteria.

Read more on msdn: Predicate Generic Delegate.

Let's get on to the example.

Example

Consider the following scenario:

public void ExecuteMethodWithCriteria1(IContext someInput)
{
	if (criteria1(someInput)) commonMethod();
}

private static bool criteria1(IContext someInput)
{
	// ...
}
public void ExecuteMethodWithCriteria2(IContext someInput)
{
	if (criteria2(someInput)) commonMethod();
}

private static bool criteria2(IContext someInput)
{
	// ...
}

so depending on an input, the methods criteria1 and criteria2 will determine whether or not to execute the commonMethod.

But what if there's more commonMethod and the criteria check is sandwiched in the middle?

public void ExecuteMethodWithCriteria1(IContext someInput)
{
	commonPreMethod();

	if (criteria1(someInput)) commonMethod();
}
public void ExecuteMethodWithCriteria2(IContext someInput)
{
	commonPreMethod();

	if (criteria2(someInput)) commonMethod();
}

There's program flow duplication now, which is not ideal. Especially if there will be ExecuteMethodWithCriteria3 and so on.

Imagine if one day we decide to introduce commonPostMethod. This will involve a manual process of updating all the methods.



The fix

An ideal way to refactor the code is to create a general method that can handle different types of criteria which has the same method signature.

In the scenario above, both criteria1 and criteria2 takes an IContext and return a boolean value. So it fits nicely with Predicate, which takes a Generic parameter to constraing the input parameter of the criteria method. Let's see the example to make it clearer.

public void ExecuteMethod(IContext someInput, Predicate  criteria)
{
	commonPreMethod();
	if (criteria(someInput))
	{
		commonMethod();
	}
}

As you can see now, we have made ourselves a general method that abstract the 2; ExecuteMethodWithCriteria1 and ExecuteMethodWithCriteria2.

Now we can do the following:

public void ExecuteMethodWithCriteria1(IContext context)
{
	ExecuteMethod(context, criteria1)
}


public void ExecuteMethodWithCriteria2(IContext context)
{
	ExecuteMethod(context, criteria2)
}

Notice we're passing the methods criteria1 and criteria2 as one of the parameter. The method obviously need to obey the signature of the Predicate that we declare in the ExecuteMethod, which was Predicate. What this means is, any method that returns a boolean and have IContext as the parameter can be used.

Nice huh?