views:

293

answers:

2

The generic list class has a .ForEach(Action<T> action) method. Now i've done some simple timings of how they both perform and it seems that the generic ForEach is the poorer performer. The (Snippet Compiler Friendly) code is below -

public static class timer{
    public static long foreachloop = 0;
    public static long Gforeachloop = 0;}

public class something{
    public List<string> myStrings = new List<string>();

    public something()
    {
     for(int i = 1; i<=5000000;i++)
     {
      myStrings.Add(i.ToString());
     }
    }}

public class cls1{
    private static List<string> Strings = new List<string>();
    private static List<string> OtherStrings = new List<string>();

    public static void RunSnippet()
    {
     something s = new something();

     Stopwatch watch = new Stopwatch();
     watch.Start();
     foreach(string x in s.myStrings)
     {
      Strings.Add(x);
     }
     watch.Stop();
     timer.foreachloop = watch.ElapsedMilliseconds;

     watch.Reset();
     watch.Start();

     s.myStrings.ForEach(delegate(string n){OtherStrings.Add(n);});

     s.myStrings.Clear();

     watch.Stop();
     timer.Gforeachloop = watch.ElapsedMilliseconds;

     WL("FOREACH-"+timer.foreachloop + ",Count = " + Strings.Count);
     WL("GFOREACH-"+timer.Gforeachloop + ",Count = " + OtherStrings.Count);
    }

    #region Helper methods

    public static void Main()
    {
     try
     {
      RunSnippet();
     }
     catch (Exception e)
     {
      string error = string.Format("---\nThe following error occurred while executing the snippet:\n{0}\n---", e.ToString());
      Console.WriteLine(error);
     }
     finally
     {
      Console.Write("Press any key to continue...");
      Console.ReadKey();
     }
    }

    private static void WL(object text, params object[] args)
    {
     Console.WriteLine(text.ToString(), args); 
    }

    private static void RL()
    {
     Console.ReadLine(); 
    }

    private static void Break() 
    {
     System.Diagnostics.Debugger.Break();
    }

    #endregion
}

FOREACH comes out at 177ms and GFOREACH at 707ms.

Now I'm guessing there's a good reason for using it but i just can't think of one. Clearly performance isn't the reason so the question is when would it be the best option?

Thanks in advance.

+5  A: 

When it looks neater.

Not a joke at all. Really, I mean it. Go with the more readable style in your case. For instance, if you just want to call a method on each item like:

list.ForEach(Console.WriteLine);

this style suits better. However, if you are having a hundred lines as the body of the loop, or you have nested loops and control flow constructs, the old style looks better.

Mehrdad Afshari
Just looks weird to me. I would find the standard foreach much easier to consume visually.
AnthonyWJones
AnthonyWJones: I think that's mostly because we're used to imperative programming. This style definitely looks more natural to programmers using functional languages.
Mehrdad Afshari
@Mehrdad - not really. Look at Haskell. When writing imperative code with side-effects (which is what this all about), they use a special monad syntax that mimics the style of imperative languages.
Daniel Earwicker
@Earwicker: As a counter-example, look at F# (OCaml): `Seq.iter`
Mehrdad Afshari
Oh, I thought you meant a *real* functional language! :P
Daniel Earwicker
@Mehrdad: Yes I am used to imperative programming, C# is primarily imperative in nature. Recently it has begun to support an ever increasing functional style of programming. Therein lies the problem, I want my familiar stuff that is imperative to continue to look as it always has and things that are functional to look functional. It helps me compartmentalise these very different ways of thinking.
AnthonyWJones
+7  A: 

This blog post from Eric Lippert gives the background:

http://blogs.msdn.com/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx

He's talking about the common suggestion of an extension method to do the same thing for IEnumerable<T>, but the philosophical objection applies to List<T>.ForEach as well.

This suggests that maybe that method was never such a good idea, although it looks "cool". It's clearer to just use foreach.

I've suggested that such methods can be thought of as a fix for the classic closure-over-loop-variable bug.

But in practice I've just got better at spotting such bugs.

Daniel Earwicker
He primarily talks about `.ForEach` on `IEnumerable<T>` (where you chain and compose stuff), not on `List<T>` where you want to perform a method on each object in the list in a single line.
Mehrdad Afshari
But there is no widely agreed sense in which `ForEach` on `IEnumerable<T>` would be composable. The obvious, consistent implementation is to mimic `List<T>.ForEach` and return void, exactly as Eric has in his blog post example. And so the same philosophical objections apply to both.
Daniel Earwicker
And regarding the loop variable closure problem, I just wish they'd made anonymous delegates and lambdas "smart" about loop variables, capturing a copy of them by default. In the extremely rare cases where the coder intends to modify the loop variable from inside the lambda, they could code around it. The other 99% of the time, we'd have had an easier life. One of those things that would be very hard to fix now, I'd imagine.
Daniel Earwicker
Probably. Your reasoning is correct IMO. However, the fact that they decided to have `List<T>.ForEach` and `Array.ForEach` (both of which appeared in .NET 2.0) and they didn't provide `Enumerable.ForEach` *extension* method (in 3.5) makes me feel there are different philosophical reasons for those. Basically, I guess `List<T>.ForEach` is designed to be used on an *already existing* collection, rather than the result of your query. And BTW, I think performance *is* really one of the reasons they have provided `ForEach` for arrays and lists in the framework
Mehrdad Afshari
I think this statement from that post on Eric's blog proves this: "It does not sit well with me to make the one and only **sequence operator** that is only useful for its side effects." [emphasis added]. Clearly, in 2.0 when `Array.ForEach` and `List<T>.ForEach` were added, they were *not* meant to be used as sequence operators; so the blog entry doesn't really apply to them.
Mehrdad Afshari
His philosophical point is about the impression given by a library facility, the direction in which it leads users. The original (no doubt good) intentions of the library designer are neither here nor there. I read Eric's point as being about the end result in terms of unnecessarily obsfucated code. Look at SO - so many times, answers have suggested `query.Select(x => x).ToList().ForEach(x => ...)`? So clearly the same idea is luring people to obsfucate their code with the existing `ForEach` methods, whether originally intended to be composable or not.
Daniel Earwicker
Furthermore, suppose the current `ForEach` methods disappeared and the extension method he defines in the blog post was dropped into the appropriate namespaces, it would do *exactly* what `List<T>.ForEach` does, would serve as an *exact* replacement for it (barring some minor performance difference). So they're very much the same thing - it makes no difference what the state of mind of a library designer in Redmond was when they implemented them. :)
Daniel Earwicker