C# Tips: IEnumerable<T>.ForEach extension method


I’m sure everyone have already used IEnumerable<T> extension methods to query your .NET collections.

Well, one thing we usually do is loop through a collection’s items and do something.

Lets say I want to write some data about portuguese people I find in my persons collection and then retrieve the maximum age value to do something else:

void Work()
{
    var persons = RetrievePersons();

    var portuguesePeople = persons.Where(p => p.Country == "Portugal");
    int maxAge;
    foreach (var p in persons.Where(p => p.Country == "Portugal"))
        Console.WriteLine("Name: {0} ; Age: {1}", p.Name, p.Age);

    maxAge = portuguesePeople.Max(p => p.Age);
    //...
}

Because the foreach statement can’t be used in a fluent API (eg. Linq) we need an extra reference so the portuguese results we’re looping through can be reused when retrieving the max age value. Being able to write code in a fluent API makes it easier to read and reduces the number of (sometimes unnecessary) variables in your code.

C# has a ForEach instance method for List<T> but most of the times you will be dealing with IEnumerable<T> references, so a ForEach extension method would be handy in these cases.

namespace System.Linq

{
    public static class EnumerableExtensionMethods
    {
        public static IEnumerable<T> ForEach<T>(this IEnumerable<T> source, Action<T> action)
        {
            if (action == null)
                throw new ArgumentNullException("action");

            foreach (var item in source)
                action(item);

            return source;
        }
    }
}

Notice the System.Linq namespace so you don’t need to include any other namespace when using IEnumerable<T>’s extension methods and also the fact that returning the source collection lets the ForEach caller reuse it again.

Now we can refactor the code above to something like this:

void Work()
{
    var persons = RetrievePersons();

    int maxAge = persons.Where(p => p.Country == "Portugal")
                        .ForEach(p => Console.WriteLine("Name: {0} ; Age: {1}", p.Name, p.Age))
                        .Max(p => p.Age);
    //...
}

Besides, since ForEach takes an Action<T> as a parameter, if you have a method with the same signature you can reuse it instead of a lambda expression like this:

int maxAge = persons.Where(p => p.Country == "Portugal")
                                 .ForEach(LogData)
                                 .Max(p => p.Age);
void LogData(Person p)
{
    Console.WriteLine("Name: {0} ; Age: {1}", p.Name, p.Age);
}

I think if you can make your code cleaner and more readable you should do it.

A lot of people are complaining about such functionality not being included in the current BCL.

If you want to read more about it please read the following post: http://blogs.msdn.com/kirillosenkov/archive/2009/01/31/foreach.aspx

4 thoughts on “C# Tips: IEnumerable<T>.ForEach extension method

  1. Perfect! Exactly what I was looking for. I especially linked the usage of the System.Linq namespace. I have not seen it done that way, but lots makes lots of sense. Thanks again.

  2. The solution kills all advantages of IEnumerable. You should use yield return item in the for each block and not return source after the for each block.

  3. You guys are right, that is more consistent with the behavior of the other IEnumerable operations and keep the lazy evaluation on the enumerable.

    I haven’t dedicated much time to create new posts and keep the current posts updated, but the ForEach extension I currently use does yield return items:)

    Thank you for posting your feedback. It’s very much welcome!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s