tags:

views:

1042

answers:

4

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();}

?

A: 

You can use lambdas which make the code more consise.

elements.ForEach( e => e.DoSomething() );

It also eliminates if statements:

elements.Where(e => e > 3).ForEach( e => e.DoSomething() );

compared to

foreach(Element element in elements) 
{ 
    if (element > 3)
        element.DoSomething();
}
Daniel A. White
foreach(Element element in elements.Where(e => e > 3))
Yuriy Faktorovich
Won't work. `Where` returns an `IEnumerable`. You could chain an extra `ToList` call in there, but that would blow performance and memory use.
Martinho Fernandes
elements.Where().ForEach doesn't work, because it's List.ForEach. Where returns IEnumerable
Sander Rijken
Why will it need to be called as a list? Thats the whole point of linq -its deferred.
Daniel A. White
It would if you have MoreLinq.
Yuriy Faktorovich
Oh. Doi........ but if linq had it, it would be like this.
Daniel A. White
@Daniel: I think you gotta review what deferred execution in LINQ means. It's not dynamic typing.
Martinho Fernandes
A: 

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
+10  A: 

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
Very interesting read. I'll +1 when I can, out of votes for now :)
Sander Rijken
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?
Kyralessa
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
I agree with Kyralessa that this is micro-(aka useless)optimization. But it's still faster.
Martinho Fernandes
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
@Gonzalo: `ArrayList` does not have a `ForEach` method. The tests are comparing `List<T>.ForEach` and `foreach` in `List<T>` and similarly for arrays.
Mehrdad Afshari
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
@nobugs: it uses for on an array internally, his own for test uses for on the List<T>
Sander Rijken
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.
Kyralessa
@Kyralessa: You're right. Did anybody say "You should be using `List<T>.ForEach` because it's faster" here?
Mehrdad Afshari
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.
Kyralessa
Wish I could vote your comment up more than once, Kyralessa.
Joel in Gö
+8  A: 

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();
}
dukk
I just got my "new stuff learned" for today. Thanks.
Martinho Fernandes
This is because under the covers, `ForEach(Action<T>)` 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.
Kyralessa
(Interestingly, Reflector also shows that somebody at Microsoft sloppily cut-and-pasted the null check from one of the `List<T>` methods with a parameter called "match". If you pass null as the `Action<T>`, it'll tell you the "match" parameter is null rather than the correct "action" parameter.)
Kyralessa