Managing collections with functors

Functors are a cool feature of .NET 2.0 which ease the management of collections.
Patrick Smacchia, in the late 2004, has written an interesting article for TheServerSide which covers many topics, and functors too. The recent advent of Linq will perhaps make them obsolete - even before they become famous! - but it is a feature to be aware of anyway.
Let me guide those of you who don't have much familiarity with delegates and anonymous methods through the topic.
Let's suppose we have a class which describes a student (I won't use properties just to save some space):
public class Student
{
public string name;
public int age;
public string course;

public Student(string name, int age, string course)
{
this.name = name;
this.age = age;
this.course = course;
}
}
Then we create a collection of students which represents an entire school and populate it with some students:
List<Student> school = new List<Student>();

school.Add(new Student("Bart", 14, "Science"));
school.Add(new Student("Lisa", 13, "Science"));
school.Add(new Student("Milhouse", 12, "Science"));
school.Add(new Student("Homer", 45, "Beer"));
After setting up the environment for testing functors, we are ready to go. Imagine we want to retrieve from our list only the students with certain characteristics, i.e. students which attend the science course, and put them inside another list. There are a couple of ways of doing that, and maybe the one which first comes to our mind is creating a method like the following:
// retrieves all the students of the science course
public static List<Student> GetScienceStudents(List<Student> allStudents)
{
List<Student> scienceStudents = new List<Student>();

foreach (Student student in allStudents)
if (student.course == "Science")
scienceStudents.Add(student);

return scienceStudents;
}
which we can call in this way:
List<Student> scienceClass1 = GetScienceStudents(school);
As you can see, that's a lot of code for such a simple task, we can do better! If you play with the intellisense on the school list, you can see that there are some methods which accept strange parameters, in particular there's the following method which looks interesting in our case:
List<Student> List<Student>.FindAll(Predicate<Student> match)
It looks promising because it returns a list of students, that's exactly what we want. So, what's that Predicate<Student> that it accepts as a parameter? It's a delegate, that is, a pointer to a method, which defines the signature of the method. This delegate is new to C# 2.0, along with other three (which I will describe later):
public delegate void Action<T>(T obj);
public delegate TOutput Converter<TInput, TOutput>(TInput input);
public delegate int Comparison<T>(T x, T y);
public delegate bool Predicate<T>(T obj);
The Predicate<T> delegate
Let's focus on the Predicate<T> delegate, because once you understand this, the others are immediate. Since the method FindAll of our list accepts such a delegate, it means that it accepts any method with the same signature. So let's write a method faithful to that signature which returns true if the supplied student attends the science course:
// returns true if the student attends the science course
public static bool IsScienceStudent(Student student)
{
return student.course == "Science";
}
Having this method, now we can call the FindAll method in this way:
List<Student> scienceClass2 = school.FindAll(new Predicate<Student>(IsScienceStudent));
Even in this case C# 2.0 makes our life easier because we don't need to create explicitly an instance of the Predicate delegate, but we can write the following, and the compiler takes care of doing that for us (in fact, the compiler emits the same code for both the previous and following method call):
List<Student> scienceClass3 = school.FindAll(IsScienceStudent);
Ok, that's something better compared to that first verbose method, but we still have to create a method just to retrieve the students of the science class.
Here come the C# 2.0 anonymous methods. Using this new feature, we don't even need to write the IsScienceStudent method, because we can supply its body directly when calling the FindAll method:
List<Student> scienceClass = 
school.FindAll(delegate(Student student)
{
return student.course == "Science";
});
This is an anonymous method, because we are creating a method by supplying his body yet without giving it a name. I'm not delving here into the topic of anonymous methods, but it's interesting to see what is happening behind the scenes, that is, what the compiler does when we declare and use an anonymous method. Using an IL disassembler like Reflector helps very much during this task. Note that the names of the members generated by the compiler may vary and the members themselves are decorated with the CompilerGenerated attribute. I'm reporting here the exact output I got when disassembling the sample program I created:
  1. A private static delegate object of type Predicate<Student> is created
    [CompilerGenerated]
    private static Predicate<Student> <>9__CachedAnonymousMethodDelegate4;
  2. A private static method faithful to the signature imposed by the Predicate<Student> delegate is created (notice that this method is the same as the IsScienceStudent method we created before!)
    [CompilerGenerated]
    private static bool <Main>b__0(Student student)
    {
    return (student.course == "Science");
    }
  3. During the execution of the program, a check is performed to see whether an instance of our delegate already exists, and in case it doesn't, a new one is created, pointing to the method created in the step above (that's why the compiler called it something like CachedAnonymousDelegate, because it's static and only one instance of it is created at runtime):
    if (Program.<>9__CachedAnonymousMethodDelegate4 == null)
    {
    Program.<>9__CachedAnonymousMethodDelegate4 =
    new Predicate<Student>(Program.<Main>b__0);
    }
  4. Finally, the FindAll method is called supplying our cached anonymous delegate as a parameter:
    List<Student> list5 = list1.FindAll(Program.<>9__CachedAnonymousMethodDelegate4);
As you can see, the compiler goes through all the steps we did before we knew about anonymous methods, thus giving us a great shortcut when writing code.

Going back to the primary topic, this is what functors are, they are parametric routines. In this case, the functor was the delegate instance created and used dinamically by the call to the FindAll method, which in turn parametrized it by calling it each time with a different input parameter, and not just in the case of an anonymous method, but even when we used the IsScienceStudent method.
As you can see, then, the Predicate<T> delegate is useful when we want to get a list of items which satisfy some conditions.

Now that you've assimilated the notion of functor we can see how the other delegates anticipated before can be useful. I won't show it from scratch using a method and then a delegate, but I will go directly to the point using anonymous methods.

The Action<T> delegate
This class is useful when you want to perform some task on all the items of the collection. Thus, le'ts suppose that, once obtained the students of the science course using one of the approaches described before, we want to change the name of the course for each of them, because the next year the science course will be called "Natural Science". Using an anonymous method with the Action<T> delegate signature this can be done using the ForEach method of our list:
scienceClass.ForEach(delegate(Student student) 
{
student.course = "Natural Science";
});
After invoking this method all the students contained in the scienceClass list will have the course field equal to "Natural Science".

The Comparison<T> delegate
This delegate class is useful when we want to compare two objects of the same type, and in particular when the comparison is a custom comparison, that is, on a complex type like a class. This delegate can be used for sorting operations. In fact, the List<T> class exposes a method called Sort which accepts, among others, an instance of the Comparison<T> delegate. Thus, let's suppose that we want to sort the students of the natural science class based on the age (in ascending order); we can write the following code:
scienceClass.Sort(delegate(Student s1, Student s2) 
{ return s1.age - s2.age; });
After this operation, the scienceClass list will contain the students sorted by ascending age.

The Converter<TInput, TOutput> delegate
This last delegate class is useful when we want to obtain a list of items of type TOutput from a list of items of type TInput. In our case, let's suppose that we want to obtain the list of the names of the students attending our famous natural science course. The TInput type will be the Student class, because we provide a list of students, and the TOutput type will be the string type, because we want in return a list of names. The method of the List<T> collection which is useful in this case is the ConvertAll method, which in fact accepts a Converter delegate:
List<string> names = 
scienceClass.ConvertAll<string>(delegate(Student student)
{
return student.name;
});
Conclusion
At the end of the post you can find a zipped sample console application I've created while writing this post.
As a side note, you must be aware that only the List<T> and Array classes expose methods which accept these delegates as parameters.
I hope this was useful and interesting. That said, for further investigation and for the complete list of methods which accept these delegates I point you to the article (which has lately become an entire chapter on his recent book) by Patrick Smacchia.

kick it on DotNetKicks.com

Attachment: Functors.zip
Published 20 August 2006 02:22 AM by simoneb
Filed under:

Comments

# DotNetKicks.com said on 19 August, 2006 08:33 PM
You've been kicked (a good thing) - Trackback from DotNetKicks.com
# DotNetKicks.com said on 19 August, 2006 10:58 PM
You've been kicked (a good thing) - Trackback from DotNetKicks.com
# Sonu said on 20 August, 2006 11:22 AM
Nice article Simone.
# simoneb said on 20 August, 2006 04:13 PM
Thanks Sonu! Glad you liked it.
# Gavin Joyce said on 21 August, 2006 02:57 PM
This article was very useful to me, I had not come across the Predicate<T> delegate before. Cheers.
# simoneb said on 21 August, 2006 03:16 PM
Thanks Gavin, I'm glad you found it useful!
# Corrado's BLogs said on 23 August, 2006 11:04 AM
# Ernesto Chaves said on 14 May, 2007 04:25 PM
Great, i'm new in anonimous methods. Once i asked a friend, What is that for? and you just answered my question.
# SimoneB's Blog said on 20 June, 2007 08:08 AM

After reading this post from Steven Smith I thought I should write something about it. Sorting a generic

# Brad C. said on 06 July, 2007 10:01 AM
Simone, really nice easy read. Thanks for continuing to post good content!
# iFX said on 27 August, 2007 04:16 AM

Collections Best Practices

# Propecia. said on 04 August, 2008 06:01 PM

Propecia online. Propecia. Propecia uk medix plus forum. Buy propecia.

This site

Search

Go

This Blog

News

Syndication

Sponsors

  • MaximumASP