Predicates are one-trick pony
delegates, used only for
methods that have a specific signature: returning a boolean value, and taking exactly one argument —
the argument type can be anything, though.
public delegate bool Predicate<T>(T alpha);
These can be useful as arguments to methods, allowing you to dynamically define the objective. For example,
"Array.Find" expects an array as the first argument (what you're searching through) and a
Predicate as the second argument (what you're searching for). Thus, I could build whatever
Predicate method I like to search for specific
Student
objects enrolled into my
Course. Let's say, a student who's done poorly on the
first exam, but also really well on the Final.
public class Course
{
Student[] enrolled;
unshort size, capacity;
public void wellDone()
{
Predicate<Student> mySearch = thereGoesMyHero;
Student myHero = Array.Find(enrolled, mySearch);
if (myHero != null)
{
myHero.print();
System.Console.WriteLine("Watch 'em as they go!");
}
}
private static bool thereGoesMyHero(Student alpha)
{
return alpha.firstExam < 65 && alpha.finalExam > 85;
}
}
The use of
predicates can also be utilized through what are called "lambda
expressions" or "anonymous functions". These are chunks of code with no formal name to be used or to refer
to them by, giving way to the creation of sort of one-time use functions. You denote these lambda
expressions using => notation. For example, I'll convert the above example into one using a
lambda expression instead.
public class Course
{
Student[] enrolled;
unshort size, capacity;
public void wellDone()
{
Student myHero = Array.Find(enrolled,
(Student alpha) => (alpha.firstExam < 65
&& alpha.finalExam > 85));
if (myHero != null)
{
myHero.print();
System.Console.WriteLine("Watch 'em as they go!");
}
}
}
Where the argument list of our lambda expression is found inside the parentheses to the left of our =>
notation, and the code of our expression found to the right. Note: I don't have to include curly braces here,
only because there's exactly one line of code being executed (a perfect setup for when to use lambda
expressions at all). The parentheses I use on the right side are only there as a personal preference, to
help me see the compound relational expression being used. If I had more instructions to be executed, I
would need to include curly braces and semi-colons, like I would for any other body of code. Such as:
Predicate<Student> myPredicate = (Student alpha) =>
{
System.Console.WriteLine("I don't know what all to do here.");
System.Console.WriteLine("But I'm doing more than one thing.");
};
You can
sometimes get away without defining the data type of the argument, which means you
should probably
always define the data type whenever possible. You can also specify for lambda
expressions to take no arguments with empty parentheses ().