ForEach, a simple but very useful extension method

This evening I was writing some code (Yay!) for an Xml based MEF catalog I am prototyping. I came across the need to invoke a set of methods on an IEnumerable<T> that was returned from a LINQ to XML query. Unfortunately no such animal exists on IEnumerable.

But this “technique” will not work for collections of any value type, and not even of string, which is a reference type!

http://wekempf.spaces.live.com wekempf

I posted on your blog as well, but the gist of my post is that to those who have used other functional languages, it is clear that ForEach should not be chainable.

http://gregbeech.com/blogs/tech/ Greg Beech

I left a full response on my blog post (where wekempf also posted a comment). The essence is that not all LINQ operations are chainable or deferred, but all operations that are chainable are also deferred, and vice versa. As it isn’t clear whether ForEach should be chainable, it isn’t clear whether it should be deferred.

http://wekempf.spaces.live.com wekempf

Both Yoshi Carroll in the comment above, and Greg Beech in his linked post comment that Linq uses deferred execution and chaining. That’s simply not true. Some parts of Linq follow this, but not all. Just one example is Average, which is neither chainable nor does deferred execution. I can’t begin to guess why ForEach was left out, but it seems obvious that’s not why.

I think the reason ForEach wasn’t added to IEnumerable (not via extension methods, but to the interface itself) was that doing so would require reimplementation of this ‘algorithm’ by anyone who implements IEnumerable. That would be painful at best and error-prone at worst.

http://Bryan.ReynoldsLive.com Bryan Reynolds

Inconsistencies. ugh.

http://kentb.blogspot.com/ Kent Boogaart

Ugh, just realised how stupid my comment was. It’s exactly what you were talking about. Sleep.

In summary, enumerating an enumerable seems like the perfect thing for System.Linq.Enumerable to do. And yet it doesn’t.

Yoshi Carroll

I’ve been (over?)thinking about this for a while now and have currently settled on using ToList().Foreach() instead. What bothers me about the implementation you show is that it breaks the conventions of Linq extension methods in that it’s not chainable, and it’s not lazy. So, while it’s possible to do .Where().Select().Foreach(), its not possible to do .Where().Foreach().Select().

This is obviously by design and it works as you needed it to but it feels a bit ambiguous to me. I tried to think of another name for it, something that would communicate the distinction, but nothing felt right.

Using .ToList().Foreach() is more explicit in what the intention is, but it has an extra method in the chain, and it’s enumerating the collection twice, so I’m not much happier with it either.