.Net 2.0's new generic collections are easy to love. In addition to helping you easily eliminate boxing issues, they've also been bequeathed with some clever delegate-based find and action capabilities. For instance, suppose you have a string collection, and want to implement a way to only pull values from that collection that start with a certain letter. It won't take you much googling to come up with a solution that uses the Predicate<> delegate and looks something like this:
public class StupidSearchMethod
{
static List<string> names = new List<string>(
new string[] { "Jeff", "Tara", "Joe" });
[STAThread]
public static void Main()
{
List<string> namesThatStartWithJ =
names.FindAll(NameStartsWithJ);
foreach (string s in namesThatStartWithJ)
Console.WriteLine(s);
Console.ReadLine();
}
private static bool NameStartsWithJ(string input)
{
return input.StartsWith("J");
}
}
Output:
Well that's ok, but am I really going to have to write a new search method when I want to find the strings that start with 'T'? It would seem so at first glance, because I am not allowed to pass any information into my predicate to help my search method. Fortunately, this is a pretty simple search, and would be amenable to using an anonymous method:
public class AnonymousSearchMethod
{
static List<string> names = new List<string>(
new string[] { "Jeff", "Tara", "Joe" });
[STAThread]
public static void Main()
{
List<string> namesThatStartWithJ = names.FindAll(
delegate(string input) { return input.StartsWith("J"); });
Console.WriteLine("Names that start with 'J':");
foreach (string s in namesThatStartWithJ)
Console.WriteLine("\t" + s);
List<string> namesThatStartWithT = names.FindAll(
delegate(string input) { return input.StartsWith("T"); });
Console.WriteLine("Names that start with 'T':");
foreach (string s in namesThatStartWithT)
Console.WriteLine("\t" + s);
Console.ReadLine();
}
}
Output:
But what if my search logic was not so trivial that I could afford to duplicate it over and over? Suppose I am looking for strings that start with a certain letter, but also have two or more vowels? Now I really need to be able to pass in extra parameters, or I'm going to just have to give up on this entirely, and code my own method. Fortunately, I can combine a more flexible search method with anonymous methods to get what I want.
public class AnonymousPlusComplexSearchMethod
{
static List<string> names = new List<string>(
new string[] { "Jeff", "Tara", "Joe" });
[STAThread]
public static void Main()
{
List<string> results = names.FindAll(
delegate(string input)
{
return CheckStartLetterAndNumVowels(input, "J", 2);
});
// Show Results Console.WriteLine("Start with 'J' and have 2 vowels:");
foreach (string s in results)
Console.WriteLine("\t" + s);
Console.ReadLine();
}
private static List<char> vowels = new List<char>(
new char[] { 'a', 'e', 'i', 'o', 'u' });
private static bool CheckStartLetterAndNumVowels(
string input, string startLetter, int numVowels)
{
if (!input.StartsWith(startLetter))
return false;
for (int i = 0; i < input.Length; i++)
if (vowels.Contains(input[i]))
numVowels--;
return numVowels == 0;
}
}
This strategy can be used with the Action<> delegates, like ForEach(Action<>) as well.