22

I'm wondering why List<T>.ForEach(Action<T>) exists.

Is there any benefit/difference in doing :

elements.ForEach(delegate(Element element){ element.DoSomething(); });

over

foreach(Element element in elements) { element.DoSomething();}

?

sebagomez
  • 9,501
  • 7
  • 51
  • 89
Cristian Diaconescu
  • 34,633
  • 32
  • 143
  • 233

3 Answers3

35

One key difference is with the .ForEach method you can modify the underlying collection. With the foreach syntax you'll get an exception if you do that. Here's an example of that (not exactly the best looking but it works):

static void Main(string[] args) {
    try {
        List<string> stuff = new List<string>();
        int newStuff = 0;

        for (int i = 0; i < 10; i++)
            stuff.Add(".");

        Console.WriteLine("Doing ForEach()");

        stuff.ForEach(delegate(string s) {
            Console.Write(s);

            if (++newStuff < 10)
                stuff.Add("+"); // This will work fine and you will continue to loop though it.
        });

        Console.WriteLine();
        Console.WriteLine("Doing foreach() { }");

        newStuff = 0;

        foreach (string s in stuff) {
            Console.Write(s);

            if (++newStuff < 10)
                stuff.Add("*"); // This will cause an exception.
        }

        Console.WriteLine();
    }
    catch {
        Console.WriteLine();
        Console.WriteLine("Error!");
    }

    Console.ReadLine();
}
Justin Long
  • 616
  • 4
  • 5
  • 3
    This is because under the covers, `ForEach(Action)` uses a for loop. In Reflector it looks like this: `for (int i = 0; i < this._size; i++) { action(this._items[i]); }` I would think you still have the potential for erratic results, though, if you do something like Insert an item ahead of your current position (e.g. at position 0) while you're looping. – Ryan Lundy Dec 28 '09 at 17:03
  • 3
    (Interestingly, Reflector also shows that somebody at Microsoft sloppily cut-and-pasted the null check from one of the `List` methods with a parameter called "match". If you pass null as the `Action`, it'll tell you the "match" parameter is null rather than the correct "action" parameter.) – Ryan Lundy Dec 28 '09 at 17:07
  • 1
    As stated [here](https://stackoverflow.com/a/226082/5197544): the implementation changed since .net 4.5 and they "both throw". – Кое Кто Jan 11 '21 at 15:55
14

It's very likely to be faster (you shouldn't choose one over the other merely because of the small performance benefits, unless you're dealing with computationally heavy number crunching or graphics application and you need to get the most out of processor cycles) and you can pass delegates directly to it which may be convenient in some cases:

list.ForEach(Console.WriteLine); // dumps the list to console.
Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
  • Very interesting read. I'll +1 when I can, out of votes for now :) – Sander Rijken Dec 17 '09 at 21:06
  • 3
    Did anyone else notice that the guy's doing *10 MILLION* iterations? And talking about savings of .03 seconds? At the point where you're doing actual work within those iterations, is that .03 seconds likely to matter? – Ryan Lundy Dec 17 '09 at 21:11
  • 4
    Kyralessa: The point is not whether it matters or not. There is a technical difference between the two that might matter for some people. That's all that matters for answering this question. – Mehrdad Afshari Dec 17 '09 at 21:12
  • 1
    I agree with Kyralessa that this is micro-(aka useless)optimization. But it's still faster. – R. Martinho Fernandes Dec 17 '09 at 21:13
  • Note that the blog linked is only testing this with `ints` which need to be unboxed when using an ArrayList, while the generic version does not need that. – Gonzalo Dec 17 '09 at 21:13
  • 2
    @Gonzalo: `ArrayList` does not have a `ForEach` method. The tests are comparing `List.ForEach` and `foreach` in `List` and similarly for arrays. – Mehrdad Afshari Dec 17 '09 at 21:18
  • Erm, doesn't that blog post recommend to use for(;;)? Which is what List<>.ForEach() does, it uses for(;;). Just not as efficient due to the delegate invocation. – Hans Passant Dec 18 '09 at 02:50
  • @nobugs: it uses for on an array internally, his own for test uses for on the List – Sander Rijken Dec 18 '09 at 10:32
  • 10
    But it's ridiculous. Who cares if you're saving 0.3 seconds over ten million iterations? What on earth are you going to do *within* those iterations that'll be so fast that the 0.3 seconds will make a difference? It's an interesting curiosity, but it has no real-world value at all. Write code to be *readable*. Use the loop construct that makes the most sense to a *human* reading the program. If you really need to save 0.3 seconds, you should be writing your program in C or C++, and not in C# or VB .NET, in the first place. – Ryan Lundy Dec 18 '09 at 14:02
  • @Kyralessa: You're right. Did anybody say "You should be using `List.ForEach` because it's faster" here? – Mehrdad Afshari Dec 18 '09 at 14:23
  • 3
    Yes, sir, I believe you did. The question was asked: "Is there any benefit [to using ForEach]?", and you replied "It's very likely to be faster." If you were to edit your answer to leave that part out, I would certainly withdraw my objections. – Ryan Lundy Dec 18 '09 at 14:43
0

It is a more convenient way/shorthand to execute an action on the items in the list, MoreLinq extends this functionality for all that implement IEnumerable.

Yuriy Faktorovich
  • 67,283
  • 14
  • 105
  • 142