Anonymous Delegates
I never thought I would use anonymous delegates mostly because I didn’t fully understand what benefit they could give me.
If I can’t get something out of a new tool or methodology, why use it. With anonymous delegates I can get a lot of great things. Specifically simple solutions to my problems
Problem
What is the best way to add filtering capability on a specialized collection? After looking at related problem sets, I found the public T Find(System.Predicate<T> match)
method which is implemented on System.Collections.Generic.List
. A Predicateis a special delegate that takes in an object, and returns true. Suppose I have a collection of names and I would like to check if a name is long. I could do the following
class Program
{
static void Main(string[] args)
{
List<string> names = new List<string>();
names.Add("Mary");
names.Add("George");
names.Add("Hank");
names.Add("Billy");
names.Add("Susan");
names.Add("Ed");
Console.WriteLine(IsLongName(names[4]));
}
static bool IsLongName(string name)
{
return name.Length > 4;
}
}
I define a method that checks if the length of the name is greater than 4. Well my method IsLongName
is in fact a System.Predicate
. Since the Find()
method takes a Predicate as the match pattern. I can pass this method to the Find()
method and get a collection of just long names. Let’s use Find()
to get a list of names that are long names. First I’ll need to wrap the IsLongNamefunction
inside of a delegate. Then I can pass that delegate into the Find()
method.
class Program
{
static void Main(string[] args)
{
List<string> names = new List<string>();
names.Add("Mary");
names.Add("George");
names.Add("Hank");
names.Add("Billy");
names.Add("Susan");
names.Add("Ed");
// WrapIsLongName up inside of Predicate delegate.
Predicate<string> longNameMethod = new Predicate<string>(IsLongName);
List<string> longNames = names.FindAll(longNameMethod);
Console.WriteLine("There are {0} long names", longNames.Count);
Console.ReadLine();
}
static bool IsLongName(string name)
{
return name.Length > 4;
}
}
The output is: There are 3 long names.
What if I need to get a list of just names that start with the letters M Gand B? No problem, create a new method that matches the signature of a Predicate and you are in business.
class Program
{
static void Main(string[] args)
{
List<string> names = new List<string>();
names.Add("Mary");
names.Add("George");
names.Add("Hank");
names.Add("Billy");
names.Add("Susan");
names.Add("Ed");
// WrapIsLongName up inside of Predicate delegate.
Predicate<string> longNameMethod = new Predicate<string>(IsLongName);
Predicate<string> mgbName = new Predicate<string>(IsMGBName);
List<string> longNames = names.FindAll(longNameMethod);
List<string> mgbNames = names.FindAll(mgbName);
Console.WriteLine("There are {0} long names.", longNames.Count);
Console.WriteLine("There are {0} MGB names.", mgbNames.Count);
Console.ReadLine();
}
static bool IsLongName(string name)
{
returnname.Length > 4;
}
static bool IsMGBName(string name)
{
if(name.StartsWith("M") || name.StartsWith("G") || name.StartsWith("B"))
{
return true;
}
returnfalse;
}
}
The console shows: There are 3 long names.There are 3 MGB names.
Clean it up
Now I see how powerful and convenient it is to be able to pass in a method that contains the logic to find an item in a list. But let’s see how anonymous delegates can help out. All that an anonymous delegate gives us is the ability to create our methods on the fly; no need to have random methods sitting around that won’t be used.
class Program
{
static void Main(string[] args)
{
List<string> names = new List<string>();
names.Add("Mary");
names.Add("George");
names.Add("Hank");
names.Add("Billy");
names.Add("Susan");
names.Add("Ed");
// WrapIsLongName up inside of Predicate delegate.
Predicate<string> longNameMethod = newPredicate<string>(IsLongName);
Predicate<string> mgbName = newPredicate<string>(IsMGBName);
List<string> longNames = names.FindAll(delegate(string name) { return name.Length > 4; });
List<string> mgbNames = names.FindAll(
delegate(string name)
{
if (name.StartsWith("M")|| name.StartsWith("G") || name.StartsWith("B"))
{
return true;
}
return false;
});
Console.WriteLine("There are {0} long names.", longNames.Count);
Console.WriteLine("There are {0} MGB names.", mgbNames.Count);
Console.ReadLine();
}
The console shows: There are 3 long names.There are 3 MGB names.
Adding Parameters
What if my special search needs an additional parameter? Just create a wrapper class
class Program
{
static void Main(string[] args)
{
List<string> names = new List<string>();
names.Add("Mary");
names.Add("George");
names.Add("Hank");
names.Add("Billy");
names.Add("Susan");
names.Add("Ed");
Predicate<string> isNameBilly = IsName("Billy");
List<string> billyNames = names.FindAll(isNameBilly);
Console.WriteLine("There are {0} Billy names.", billyNames.Count);
Console.ReadLine();
}
static Predicate<string> IsName(string name)
{
return delegate(string item)
{
return item.Equals(name);
};
}
}
I can create a method that returns a method. What is amazing about this is the ability to pass extra parameter into the dynamic method.