Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# foreach vs functional each [closed]

Which one of these do you prefer?

foreach(var zombie in zombies)
{
    zombie.ShuffleTowardsSurvivors();
    zombie.EatNearbyBrains();
}

or

zombies.Each(zombie => {
    zombie.ShuffleTowardsSurvivors();
    zombie.EatNearbyBrains();
});
like image 240
whatupdave Avatar asked Jan 07 '10 22:01

whatupdave


4 Answers

The first. It's part of the language for a reason.

Personally, I'd only use the second, functional approach to flow control if there is a good reason to do so, such as using Parallel.ForEach in .NET 4. It has many disadvantages, including:

  • It's slower. It's going to introduce a delegate invocation at each element, just like you did foreach (..) { myDelegate(); }
  • It's non-standard, so will be more difficult to understand by most developers
  • If you close over any locals, you're going to force the compiler to make a closure. This can lead to strange issues if there's threading involved, plus adds completely unnecessary bloat to the assembly.

I see no reason to write your own syntax for a flow control construct that already exists in the language.

like image 57
Reed Copsey Avatar answered Sep 17 '22 11:09

Reed Copsey


Here you're doing some very imperative things like writing a statement rather than an expression (as presumably the Each method returns no value) and mutating state (which one can only assume the methods do, as they also appear to return no value) yet you're trying to pass them off as 'functional programming' by passing a collection of statements as a delegate. This code could barely be further from the ideals and idioms of functional programming, so why try to disguise it as such?

As much as I like multi-paradigm languages such as C#, I think they are easiest to understand and maintain when paradigms are mixed at a higher level (e.g. an entire method written in either a functional or an imperative style) rather than when multiple paradigms are mixed within a single statement or expression.

If you're writing imperative code just be honest about it and use a loop. It's nothing to be ashamed of. Imperative code is not an inherently bad thing.

like image 39
Greg Beech Avatar answered Sep 19 '22 11:09

Greg Beech


Second form.

In my opinion, the less language constructs and keywords you have to use, the better. C# has enough extraneous crud in it as it is.

Generally the less you have to type, the better. Seriously, how could you not want to use "var" in situations like this? Surely if being explicit was your only goal, you'd still be using hungarian notation... you have an IDE that gives you type information whenever you hover over... or of course Ctrl+Q if you're using Resharper...

@T.E.D. The performance implications of a delegate invocation are a secondary concern. If you're doing this a thousand terms sure, run dot trace and see if it's not acceptable.

@Reed Copsey: re non-standard, if a developer can't work out what ".Each" is doing then you've got more problems, heh. Hacking the language to make it nicer is one of the great joys of programming.

like image 30
mlangsworth Avatar answered Sep 19 '22 11:09

mlangsworth


The lamda version is actually not slower. I just did a quick test and the delegate version is about 30% faster.

Here is the codez:

class Blah {
    public void DoStuff() {
    }
}

        List<Blah> blahs = new List<Blah>();
        DateTime start = DateTime.Now;

        for(int i = 0; i < 30000000; i++) {
            blahs.Add(new Blah());
        }

        TimeSpan elapsed = (DateTime.Now - start);
        Console.WriteLine(string.Format(System.Globalization.CultureInfo.CurrentCulture, "Allocation - {0:00}:{1:00}:{2:00}.{3:000}",
         elapsed.Hours,
         elapsed.Minutes,
         elapsed.Seconds,
         elapsed.Milliseconds));

        start = DateTime.Now;

        foreach(var bl in blahs) {
            bl.DoStuff();
        }

        elapsed = (DateTime.Now - start);
        Console.WriteLine(string.Format(System.Globalization.CultureInfo.CurrentCulture, "foreach - {0:00}:{1:00}:{2:00}.{3:000}",
         elapsed.Hours,
         elapsed.Minutes,
         elapsed.Seconds,
         elapsed.Milliseconds));

        start = DateTime.Now;

        blahs.ForEach(bl=>bl.DoStuff());

        elapsed = (DateTime.Now - start);
        Console.WriteLine(string.Format(System.Globalization.CultureInfo.CurrentCulture, "lambda - {0:00}:{1:00}:{2:00}.{3:000}",
         elapsed.Hours,
         elapsed.Minutes,
         elapsed.Seconds,
         elapsed.Milliseconds));

OK, So I've run more tests and here are the results.

  1. The order of the execution(forach, lambda or lambda, foreach) didn't make much difference, lambda version was still faster:

    foreach - 00:00:00.561
    lambda - 00:00:00.389
    
    lambda - 00:00:00.317
    foreach - 00:00:00.337
  2. The difference in performance is a lot less for arrays of classes. Here are the numbers for Blah[30000000]:

    lambda - 00:00:00.317 
    foreach - 00:00:00.337
  3. Here is the same test but Blah being a struct:

    Blah[] version 
    lambda - 00:00:00.676 
    foreach - 00:00:00.437 
    
    List version:
    lambda - 00:00:00.461
    foreach - 00:00:00.391
  4. Optimized build, Blah is a struct using an array.

    lambda - 00:00:00.426
    foreach - 00:00:00.079

Conclusion: There is no blanket answer for performance of foreach vs lambda. The answer is It depends. Here is a more scientific test for List<T>. As far as I can tell it's pretty damn efficient. If you are really concerned with performance use for(int i... loop. For iterating over a collection of a thousand customer records (example) it really doesn't matter all that much.

As far as deciding between which version to use I would put potential performance hit for lambda version way at the bottom.

Conclusion #2 T[] (where T is a value type) foreach loop is about 5 times faster for this test in an optimized build. That's the only significant difference between a Debug and Release build. So there you go, for arrays of value types use foreach, everything else - it doesn't matter.

like image 32
Igor Zevaka Avatar answered Sep 18 '22 11:09

Igor Zevaka