In my recent post about coding styles one particular thing provoked the majority of feedback and discussions: the ForEach extension method on IEnumerable<T>. Justin Etheredge has a good post about this method here. StackOverflow.com also has a good question: Why is there not a ForEach extension method on the IEnumerable interface?
Note: If you’d like this method to be added to .NET 4.0, go vote here: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=279093
Recently I also went ahead and logged a suggestion against the BCL team to add this method in .NET 4.0, and got some feedback from Melitta Andersen and Justin van Patten. Essentially, the BCL team’s considerations boil down to what we’ve discussed in my recent post:
- it encourages state mutation
- it’s hard to set a breakpoint inside the lambda/delegate body (and other debugging issues)
- it’s not clear where to place this method (it’s not exactly part of LINQ, it’s just a helper on IEnumerable<T>), also it doesn’t allow to chain calls
Mads Torgersen (representing the language design team) was also reluctant about adding this method because of similar concerns (functional impurity etc). I myself in my previous post was enumerating various downsides of using this method.
And still I think we should add it.
My thinking is the following. The ultimate purpose of the BCL is to help avoid code duplication by introducing a common reusable set of functionality so that people don’t have to reinvent the wheel by writing their own collections, sorters, etc. A lot of people will use ForEach anyway, and if we don’t provide it in the framework, they will have to re-implement it in every new project. Also, by providing the ForEach method out of the box, we’re not forcing anyone to actually go ahead and use it – people will still have the choice and be warned about the downsides of ForEach. It’s just when they will use it anyway (and this happens a lot), they will be able to consume a ready-made one. The use of having it (in my opinion) by far overweights the downsides of using it inappropriately.
ForEach looks really good with very simple snippets, such as:
Some developers forget that you can use this shorter syntax instead of:
myStrings.ForEach(s => Console.WriteLine(s));
Another ForEach advantage is that it allows you to extract the body of the loop into a separate place and reuse it by just calling into it.
Also, given the fact that List<T> already has it, it seems unfair that IEnumerable<T> doesn’t. This is an unnecessary limitation (that’s what I think).
Chris Tavares says:
I suspect the reason that this didn't exist before is that you can't use it from VB. VB only support lambda expressions, while the foreach method requires a lambda *statement*.
Well the good news is that we are introducing statement lamdbas in VB 10.0 so this shouldn’t be an issue at all.
I’m also pretty sure that one can also overcome the debugging difficulties of ForEach with tooling support, such as [DebuggerStepThrough] and “Step Into Specific”. Debugging problems are not language/libraries problem per se, it’s a tooling problem, and tooling should always be fixed/improved to satisfy languages and libraries.
A lot of people are asking for the ForEach extension method – this is probably one most wanted piece of API:
- Connect feedback item: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=279093
- Why is there not a ForEach extension method on the IEnumerable interface? (StackOverflow.com)
- Extension Method – ForEach
- My Technobabble - ForEach, a simple but very useful extension method (Glenn Block)
- “However, just because LINQ is implemented using extension methods does not mean that extension methods must be used in the same way and return a value. Writing an extension method to expose common functionality that does not return a value is a perfectly valid use.” (Scott Dorman)
- “No one has yet pointed out that ForEach<T> results in compile time type checking where the foreach keyword is runtime checked.”
- Suggestion: http://social.msdn.microsoft.com/forums/en-US/linqprojectgeneral/thread/2dddb3b1-5ea3-41a6-880a-f994acf0a68b/ (Joe Albahari)
- “There's also no equivalent on System.Linq.Enumerable, which has irked me on several occasions.”
- “I've often wondered why a ForEach wasn't added for IEnumerable<T>. It's very handy on List<T>...” (Peter Ritchie)
- http://davesquared.blogspot.com/2008/03/ienumerable-and-foreach.html (David Tchepak)
- “Indeed, it's seems incredible to me they just forgot that feature. Do you know if there is any good reason behind, or do you believe it's really an omission?” (Yann Trevin)
- “I am surprised that the ForEach() was left off of the IEnumerable<T> interface as well.”
- Extension Methods Better Practices
- Why doesn't System.Linq.Enumerable have a ForEach extension method
- http://msmvps.com/blogs/jon_skeet/archive/2008/10/23/what-other-enumerable-extension-methods-would-you-like-to-see.aspx (Jon Skeet)
- A Limitation of Lambda Expressions and Overloaded Extension Methods (Omer van Kloeten)
- Kirill Osenkov- Extension methods- one of my favorite C# 3.0 features (ha! That’s myself in 2007!)
My questions for you folks are:
- What do you think? Should this method be added to BCL?
- If yes, where? System.Linq.Enumerable? System.Collections.Generic.Extensions? Anywhere else?
There are also variations on this extension method:
- returning IEnumerable<T> to allow the ForEach calls to chain
- accepting an Action<T, int> where the second parameter is the index of an item
- Kevin’s Apply method
If you ask me, only the simplest overload should be added, because Select is a better choice for chaining calls. Also this would encourage people to use the method in the simplest scenarios, where the downsides of doing so are negligible.
Finally, one argument I have is that googling “foreach extension method” yields 1 590 000 results, which I think is a pretty good indication that the feature has high demand.