ForEach

ForEach

  • Comments 13

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:

myStrings.ForEach(Console.WriteLine);

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:

My questions for you folks are:

  1. What do you think? Should this method be added to BCL?
  2. 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.

  • The final argument looks a bit weaker if you include the quotes round the terms, i.e. looking for the phrase "foreach extension method" - only 273 hits then.

    It's perfectly natural for the word "foreach" to appear with "extension methods":

    "Extension methods like Select allow you to iterate over a projection with foreach" etc.

    That's not expressing an opinion on the goodness or otherwise of introducing Enumerable.ForEach - just saying that the Google argument doesn't really hold much water IMO.

    Jon

  • My main complaint about the ForEach extension method is that internally (use Reflector) it uses a for-loop to iterate over the collection. This approach allows the user to modify the collection (i.e. add or remove elements) from within the ForEach loop, which messes up the iterator and could unexpectedly cause the "ForEach" to skip elements of the collection. I'm not sure why they used a for-loop within the ForEach, but it could potentially lead to some disasterous and difficult to debug problems.

  • List<T> already has it (killing the VB argument?).

    State mutation? This is C#. I don't see how having a method versus the foreach keyword is much different.

    Debugging? Anonymous method debugging is something VS2010 better do much better anyways. ForEach would hardly be the only use case there.

    A chaining version can be handy, but it should have another name.

    F# has has "iter" in the Array, List, and Seq modules to achieve the same thing, so I really don't get the "functional" argument. (And "iteri" for the indexed version.)

    My guess is it just didn't "fit in" with querying, hence putting it in "LINQ" doesn't work. But, it doesn't really matter where it goes, because extension methods effectively throw away all that scoping.

  • I find it interesting that the Parallel Extensions include both <a href="http://www.danielmoth.com/Blog/2009/01/parallelising-loops-in-net-4.html">For and ForEach methods</a> that could easily be added (either internally or via extension methods) to IEnumerable (or just use <a href="http://www.danielmoth.com/Blog/2009/01/plinq.html">PLINQ</a>).

    In terms of use, I really liked <a href="http://blogs.msdn.com/kevinpilchbisson/archive/2008/10/27/removing-elements-from-a-dictionary.aspx">Kevin's Apply method</a>, as it appears more as a continuation. However, we must now ask whether we want such a method or whether we want to be more explicit about using <a href="http://blogs.msdn.com/wesdyer/archive/2008/01/11/the-marvels-of-monads.aspx">continuations</a> (e.g. with a .ToContinuation() extension), adding the action in the Select method (e.g. <code>list.Select(item => action(item))</code>), or an overloaded Select method.

  • I definitely think it should be added.  My own personal utility library (BclExtensions link below) has a version.  I use it constantly it my applications and definately would like to see it added.  

    I did end up having to name it Iterate vs. ForEach to avoid conflicts.

    http://code.msdn.com/bclextras

  • I think that you should definitely add two immediately-executed non-chainable overloads because the semantics are clearest (if you return anything other than void then it isn't clear whether the method is lazy or not). The version with an index is also rather useful.

    void ForEach<T>(this IEnumerable<T> source, Action<T> action) { ... }

    void ForEach<T>(this IEnumerable<T> source, Action<T, int> action) { ... }

    Somewhat less useful but occasionally valuable are the param array versions of these. I'm fairly ambivalent about whether these are added but on the other hand they're simple to implement and test, and would provide additional functionality, which is what the BCL is there for.

    void ForEach<T>(this IEnumerable<T> source, params Action<T>[] actions) { ... }

    void ForEach<T>(this IEnumerable<T> source, params Action<T, int>[] actions) { ... }

  • I think people who want it mainly use it for simply actions like

    collection.ForEach(item -> item.Foo());

    I'm fine with that case except the performance penalty of an extra delegate.

    If you add it to BCL, that means you encourage people to use it. But I don't think that's what you want.

  • I think this method is useful and it's definitely worth adding it. I want to be in System.Linq.Enumerable.

    I don't want to be chainable because I believe iterating over the collection is the last thing to do. I've posted my reasoning on my blog (http://ppetrov.wordpress.com/2009/01/22/foreach-method-on-ienumerable/)

  • I wanted a ForEach method when I first started using Linq, but since then I've gone off the idea.  It just doesn't seem like the C# way. "The C# way" has been the classic foreach keyword for a long time now.

    I've actually found it helpful to tell new developers, "If you want to select stuff, use linq; if you want to mutate state use a foreach (keyword) loop".

    Also, the ForEach method just doesn't read so well left-to-right.  Compare "foo.ForEach(method)" with foo.Where(method)".  Perhaps it partly depends on what you're used to, but foo.Where seems to read well to me, but foo.ForEach doesn't.

    Having said that, I really like the idea that you can use implicit method group conversion, as in foo.ForEach(method).  If something is going to be added, what about adding something to the _language_, so that we could write something with still reads with the same left-to-right order as the foreach we are all used to, but with allow method group conversion.

    For instance, let's say that all we want is a shorter, strongly-typed-checked version of the classic foreach loop. Perhaps something like this:

    foreach(myStrings, Console.WriteLine);

    which would be exactly equivalent to this:

    foreach s in myStrings

     Console.WriteLine(s);

    The only difference would be that the version which uses foreach(IEnumerable<T>, Action<T>) could, and should, be type-checked at compile time.

    Of course, you could also write the Action<T> out in lambda style, like this:

    foreach(myStrings, s => Console.WriteLine(s));

    Overall, this approach keeps the familiar foreach keyword, keeps the familar left-to-right order, and is just as compact as other proposals.  

    One concern may be the notion of overloading the syntax of existing keyword (foreach).  However, we already have a precedent for such a change:  VB.NET recently introduced a function-like version of the "if" statement, as its new ternary operator.  The old version of the "if" remains available and works like it always did.  Likewise, under the foreach(IEnumerable<T>, Action<T>) proposal, the old version of foreach would remain available and work the same way that it always did.

  • @John, I agree quite a bit with "It just doesn't seem like the C# way." Except that the answer isn't "oh well", but "find something that goes the right way" :)

  • In LINQ in Action , we discuss about the missing ForEach query operator. This is in Chapter 5 "Beyond

  • In LINQ in Action , we discuss about the missing ForEach query operator. This is in Chapter 5 "Beyond

  • In my first version of my functional programming tutorial , I discussed a ForEach extension method, but

Page 1 of 1 (13 items)
Leave a Comment
  • Please add 1 and 6 and type the answer here:
  • Post