Saturday, July 29, 2006

Why closure methods on collections matter

I like the new closure methods (like ForEach and ConvertAll) on .NET 2.0 classes such as List<T> and Array, because from an OO standpoint, it really should be the responsibility of a collection to know how to traverse its own elements. However, I've always had a tough time justifying why you'd want to do...

employees.ForEach(delegate(Employee e) 
{
    Console.WriteLine(e);
});


... instead of...

foreach (Employee e in employees)
{
    Console.WriteLine(e);
}


In this case, it doesn't look like you gain anything.

Recently, I came across a situation which made me see the difference more clearly. Let's say you have a list of employees, and now, you want to remove all managers. Happily, we write...

foreach (Employee e in employees) 
{ 
    if (employee.Manager)
    {
        employees.Remove(e);
    }
}


This causes an exception because you can't modify a collection while you're iterating through it. OK, how about...

for (int i = 0; i < employees.Count; i++) 
{
    if (employees[i].Manager)
    {
        employees.RemoveAt(i);
    }
}


This doesn't throw an exception, but also doesn't work properly because we're modifying the count in the loop and our index will skip over elements. OK, so the solution is...

for (int i = employees.Count - 1; i >= 0; i--) 
{
    if (employees[i].Manager)
    {
        employees.RemoveAt(i);
    }
}


That's great! However, we now have to worry about remembering that a forward loop through a collection to conditionally remove elements semantically doesn't work. If you forget, no exception will be thrown, and only running unit tests or executing the program will reveal this.

On the other hand, we wouldn't have to remember this little gotcha if we simply relied on the RemoveAll method...

employees.RemoveAll(delegate(Employee e) { return e.Manager; });


In a way, this situation is similar to why we don't hesitate to use the Sort(IComparer) or Sort(Comparison<T>) methods, versus getting the elements and writing our own sorting algorithm. Do we have to care how the collection's sort works? Generally, no, since that's the responsibility of the collection.

FYI, check out the List<T>.RemoveAll method in .NET Reflector. It's not using a reverse for-loop.

10 comments:

taowen said...

hey~ I thought you are Eric Du at the first glance. Are you from China?

Eric Liu said...

Hi tawowen: No, I'm not from China... I've been in Calgary most of my life.

Anonymous said...

Where's the new blog?

Ajay Kumar K.S. said...

hi Eric , ur in thought works right ? i have my campus placements next week. thoughtworks is visiting our campus. can u please let me know if i can get some sample campus placement questions . i'm unable to find it anywhere :( . it would be of great help. my email id is : ajaykumar127@yahoo.co.in
thank you :)

Jethro123 said...

Nice Post. This could be ver yuseful. Thanks. Bespoke Software Solutions

Android app developer said...

Excellent post.I like your blog tips.Nice to read about your post.Good work.

Software Development said...

Very nice post..Thanks for sharing your experience.

Mike Jonsen said...

Hi, very nice post. This is an informative post for me. thanks
Web Software Development

pratham vision said...

I like your view,The statement you have kept in your stuff is really attractive for People like me.thanks for share to it.

Software Development Company India

Web design company Delhi said...

Thanks for sharing useful information. I have bookmark your blog for future updates. Keep Posting..