55

Possible Duplicate:
Why is there not a ForEach extension method on the IEnumerable interface?

I've noticed when writing LINQ-y code that .ForEach() is a nice idiom to use. For example, here is a piece of code that takes the following inputs, and produces these outputs:

{ "One" } => "One"
{ "One", "Two" } => "One, Two"
{ "One", "Two", "Three", "Four" } => "One, Two, Three and Four";

And the code:

private string InsertCommasAttempt(IEnumerable<string> words)
{
    List<string> wordList = words.ToList();
    StringBuilder sb = new StringBuilder();
    var wordsAndSeparators = wordList.Select((string word, int pos) =>
        {
            if (pos == 0) return new { Word = word, Leading = string.Empty };
            if (pos == wordList.Count - 1) return new { Word = word, Leading = " and " };
            return new { Word = word, Leading = ", " };
        });

    wordsAndSeparators.ToList().ForEach(v => sb.Append(v.Leading).Append(v.Word));
    return sb.ToString();
}

Note the interjected .ToList() before the .ForEach() on the second to last line.

Why is it that .ForEach() isn't available as an extension method on IEnumerable<T>? With an example like this, it just seems weird.

fhcimolin
  • 616
  • 1
  • 8
  • 27
Olema
  • 561
  • 1
  • 4
  • 4
  • 9
    It is not even included in IList interface but the List impementation – Marc Wittke Dec 16 '09 at 07:07
  • 3
    The problem is that IEnumerable and possibly IList are lazy, so you would still need to call .ToList() or .ToArray() after the .ForEach to ensure that the items are actually iterated. If the values are not used then the lazy evaluation of IEnumerable will result in your ForEach not being preempted when you think it was. That's why there is also an Array.ForEach, because once you have called ToArray() then the compiler can be sure that the list has been iterated. With ForEach(this IEnumerable...) you cannot be sure. – Jim Sep 12 '12 at 07:05

10 Answers10

40

According to Eric Lippert, this is mostly for philosophical reasons. You should read the whole post, but here's the gist as far as I'm concerned:

I am philosophically opposed to providing such a method, for two reasons.

The first reason is that doing so violates the functional programming principles that all the other sequence operators are based upon. Clearly the sole purpose of a call to this method is to cause side effects.

The purpose of an expression is to compute a value, not to cause a side effect. The purpose of a statement is to cause a side effect. The call site of this thing would look an awful lot like an expression (though, admittedly, since the method is void-returning, the expression could only be used in a “statement expression” context.)

It does not sit well with me to make the one and only sequence operator that is only useful for its side effects.

The second reason is that doing so adds zero new representational power to the language.

Andrew Cooper
  • 32,176
  • 5
  • 81
  • 116
Justin R.
  • 23,435
  • 23
  • 108
  • 157
  • 9
    Wouldn't be nice if your own personal programming preferences could affect millions of others? Such power this "Eric Lippert" wields... – Frank Krueger Nov 19 '09 at 19:02
  • 13
    Yes, if only each of us could design a compiler used by millions of others :) – Justin R. Nov 19 '09 at 19:39
  • But then we don't get the index, and we're reduced to a regular for loop. Right? – JoeB Oct 16 '12 at 03:02
  • 1
    This is a great explanation of why the method doesn't exist and why you might think twice about adding it in yourself. – tarrball Jul 26 '17 at 14:18
40

Because ForEach(Action) existed before IEnumerable<T> existed.

Since it was not added with the other extension methods, one can assume that the C# designers felt it was a bad design and prefer the foreach construct.


Edit:

If you want you can create your own extension method, it won't override the one for a List<T> but it will work for any other class which implements IEnumerable<T>.

public static class IEnumerableExtensions
{
  public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
  {
    foreach (T item in source)
      action(item);
  }
}
Community
  • 1
  • 1
Samuel
  • 37,778
  • 11
  • 85
  • 87
5

Because ForEach() on an IEnumerable is just a normal for each loop like this:

for each T item in MyEnumerable
{
    // Action<T> goes here
}
Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • 1
    Well, I can see the appeal of embedding a lambda in a ForEach rather than doing that. But it does seem like a minor point. – Promit Apr 28 '09 at 22:58
  • Wouldn't it be nice to be able to do it inline the same way you can on a list too though? I've also wondered this in the past, but never thought to bring it up. – BenAlabaster Apr 28 '09 at 22:59
  • I guess the question is, why didn't they define .ForEach as part of the IEnumerable interface? You can foreach over other list types *or* you can use the .ForEach() method, I don't really see the difference. – BenAlabaster Apr 28 '09 at 23:02
  • Yeah, that was my thought as well. – Olema Apr 28 '09 at 23:02
  • 1
    The only real answer you will get it: because. For whatever reason, the designers decided not to add it with IEnumerable. But that doesn't stop you from implementing it yourself if you wish. – Samuel Apr 28 '09 at 23:05
  • True, if you add an extension method it's pretty easy, but all you're gonna be doing is the foreach inside it which kind of defeats the purpose but whatever. :) – BenAlabaster Apr 28 '09 at 23:09
  • @balabaster: "you can foreach over list types" ... That works because those list types meet the requirements to implement IEnumerable. – Joel Coehoorn May 04 '09 at 21:04
3

ForEach isn't on IList it's on List. You were using the concrete List in your example.

Joshua Belden
  • 10,273
  • 8
  • 40
  • 56
3

I am just guessing here , but putting foreach on IEnumerable would make operations on it to have side effects . None of the "available" extension methods cause side effects , putting an imperative method like foreach on there would muddy the api I guess . Also, foreach would initialize the lazy collection .

Personally I've been fending off the temptation to just add my own , just to keep side effect free functions separate from ones with side effects.

Surya
  • 4,922
  • 8
  • 41
  • 54
0

I honestly don't know for sure why the .ForEach(Action) isn't included on IEnumerable but, right, wrong or indifferent, that's the way it is...

I DID however want to highlight the performance issue mentioned in other comments. There is a performance hit based on how you loop over a collection. It is relatively minor but nevertheless, it certainly exists. Here is an incredibly fast and sloppy code snippet to show the relations... only takes a minute or so to run through.

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Start Loop timing test: loading collection...");
        List<int> l = new List<int>();

        for (long i = 0; i < 60000000; i++)
        {
            l.Add(Convert.ToInt32(i));
        }

        Console.WriteLine("Collection loaded with {0} elements: start timings",l.Count());
        Console.WriteLine("\n<===============================================>\n");
        Console.WriteLine("foreach loop test starting...");

        DateTime start = DateTime.Now;

        //l.ForEach(x => l[x].ToString());

        foreach (int x in l)
            l[x].ToString();

        Console.WriteLine("foreach Loop Time for {0} elements = {1}", l.Count(), DateTime.Now - start);
        Console.WriteLine("\n<===============================================>\n");
        Console.WriteLine("List.ForEach(x => x.action) loop test starting...");

        start = DateTime.Now;

        l.ForEach(x => l[x].ToString());

        Console.WriteLine("List.ForEach(x => x.action) Loop Time for {0} elements = {1}", l.Count(), DateTime.Now - start);
        Console.WriteLine("\n<===============================================>\n");

        Console.WriteLine("for loop test starting...");

        start = DateTime.Now;
        int count = l.Count();
        for (int i = 0; i < count; i++)
        {
            l[i].ToString();
        }

        Console.WriteLine("for Loop Time for {0} elements = {1}", l.Count(), DateTime.Now - start);
        Console.WriteLine("\n<===============================================>\n");

        Console.WriteLine("\n\nPress Enter to continue...");
        Console.ReadLine();
    }

Don't get hung up on this too much though. Performance is the currency of application design but unless your application is experiencing an actual performance hit that is causing usability problems, focus on coding for maintainability and reuse since time is the currency of real life business projects...

Aaron
  • 17
  • 1
0

ForEach is implemented in the concrete class List<T>

Chad Grant
  • 44,326
  • 9
  • 65
  • 80
  • 3
    It's definitely possible to add extension methods to interfaces. – Rex M Apr 28 '09 at 22:53
  • Rex to the rescue with negative comments again! yeah my stalker returns – Chad Grant Apr 28 '09 at 22:55
  • 1
    How is that a negative comment? – Rex M Apr 28 '09 at 22:56
  • 2
    @Rex: Since he edited it, you comment looks wrong. But it originally said you cannot have extension methods on interfaces, which is oh so very wrong. – Samuel Apr 28 '09 at 23:00
  • I admit I made an incorrect statement in haste and corrected it. I generally avoid extension methods because they complicate code in the same manner that operator overloads do ... they're hard to "know" they're there. So I'm not as experienced with using them. – Chad Grant Apr 28 '09 at 23:01
  • You can add extension methods to interfaces: IEnumerable itself has many fine examples. – Olema Apr 28 '09 at 23:01
  • @Samuel he also seems to confuse "being wrong on questions I'm also attracted to" with "stalking" ;) – Rex M Apr 28 '09 at 23:02
  • I retracted the downvote since the answer is no longer correct btw. I try to be helpful, not actually out to get you. Promise. – Rex M Apr 28 '09 at 23:03
  • I don't care about the down votes I guess. But is there an editing "etiquette"? I edit a lot. I could see someone copy / pasting correct answers just to get some easy upvotes – Chad Grant Apr 28 '09 at 23:09
  • @Deviant I've never seen that happen. It might from time to time but I imagine it's very rare. It's too easy to see who said it first, and if all you care about is rep points, enough to try to steal them, you also care enough to not want the wrath of a ton of downvotes if they catch you. – Rex M Apr 28 '09 at 23:14
0

Just a guess, but List can iterate over its items without creating an enumerator:

public void ForEach(Action<T> action)
{
    if (action == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    for (int i = 0; i < this._size; i++)
    {
        action(this._items[i]);
    }
}

This can lead to better performance. With IEnumerable, you don't have the option to use an ordinary for-loop.

Rauhotz
  • 7,914
  • 6
  • 40
  • 44
  • In almost all cases, the performance hit will be negligible. And duh you cannot use a for loop, because IEnumerable has no concept of an index. – Samuel Apr 28 '09 at 23:01
  • It CAN make a difference. I had situations where going from 'foreach' to 'for' resulted in a speedup of 10 just because the garbage collector didn't have to collect the enumerator any more. – Rauhotz Apr 28 '09 at 23:07
  • 1
    I call BS on that. Either show proof or you're doing something very very wrong with your loops. – Samuel Apr 28 '09 at 23:20
  • 1
    @Rauhotz you must have a really shitty enumerator – Rex M Apr 28 '09 at 23:22
  • What shitty enumerator? It's the enumerator that is returned from List.GetEnumerator(), an object, that has to be created and collected by the GC afterwards. Use it in a tight loop within a heavily multithreaded application and you will see the GC to take 90% of the CPU time. When using List.Foreach, no object will be created and that can make the difference. – Rauhotz Apr 29 '09 at 07:01
0

LINQ follows the pull-model and all its (extension) methods should return IEnumerable<T>, except for ToList(). The ToList() is there to end the pull-chain.

ForEach() is from the push-model world.

You can still write your own extension method to do this, as pointed out by Samuel.

tofi9
  • 5,775
  • 4
  • 29
  • 50
-1

It's called "Select" on IEnumerable<T> I am enlightened, thank you.

JP Alioto
  • 44,864
  • 6
  • 88
  • 112
  • ack! Am I wrong? Enlighten me! – JP Alioto Apr 28 '09 at 22:55
  • Not exactly - in the example above, wordsAndSeparators.Select(v =>sb.Append(v.Leading).Append(v.Word)); doesn't give you what you'd expect, because of pipelining. You'd have to say: wordsAndSeparators.Select(v => sb.Append(v.Leading).Append(v.Word)).ToList(); – Olema Apr 28 '09 at 23:00
  • 1
    ForEach takes an Action which is a delegate that eats Ts and returns void; consequently, ForEach returns void. Select takes a Func which is a delegate that eats Ts and returns TResults; consequently, Select returns an IEnumerable. – jason Apr 28 '09 at 23:03