Friday, February 11, 2011

Extension methods: For<T> and ForEach<T>

I noticed that List has a method called ForEach, that takes an Action and applies it to all elements in the collection. This is not an extension method, it's part of List.

I wondered why it's not part of the LINQ Extension methods. The best I could come up with was that IEnumerables can be infinite, so a non-terminable ForEach would cause problems. Of course, we can do these with standard foreach loops, so I see no harm in including it if you know what you're getting into. Also, it should be possible to provide a terminator clause to exit the foreach loop as a Func.

With that said, I also felt the need to be able to simulate a simple for loop. An indexed-foreach, if you will. So here are three extension methods I've come up with:

public static void For<T>(this IEnumerable<T> items, Action<int, T> action) {
    for (int i = 0; i < items.Count(); i++)
        action(i, items.ElementAt(i));
}

public static void ForEach<T>(this IEnumerable<T> items, Action<T> action) {
    foreach (T item in items)
        action(item);
}

public static void ForEach<T>(this IEnumerable<T> items, Action<T> action, Func<T, bool> breakWhen) {
    foreach (T item in items) {
        if (breakWhen(item))
            break;
        action(item);
    }
}

Here's an example of use:

var numbers = Enumerable.Range(10, 10);
numbers.For((i, x) => Console.WriteLine("Index: {0}, Value: {1}", i, x));
numbers.ForEach(x => Console.WriteLine(x));
var allReal = Extensions.Real(0);
allReal.ForEach(x => Console.WriteLine(x), x => x > 9);

Extensions.Real(0) is just a method that returns an IEnumerable that starts at 0 and counts up. There's no exit condition, so if you tried to iterate through the whole thing, it'd just go until it hit an integer overflow. But it shows we can provide an exit condition for a ForEach loop.