views:

637

answers:

6

Say I have a class:

public class MyClass
{
   ...
}

and a webservice method that returns an IEnumerable<MyClass>

The consumer of the webservice defines some method:

public void DoSomething(MyClass myClass)
{
   ...
}

Now, the consumer can call DoSomething on the result of the webservice method in two ways:

var result = // web service call

foreach(var myClass in result)
{
   DoSomething(myClass);
}

or:

var result = // web service call

result.ToList().ForEach(DoSomething);

Needless to say I much prefer the second way since it is much shorter and more expressive (once you get used to the syntax, which I have).

Now, the web service method only exposes an IEnumerable<MyClass>, but it actually returns a List<MyClass> which (AFAIK) means that the actual serialized object is still a List<T>. However, I have found (using reflector) that the Linq method ToList() makes a copy of all the objects in the IEnumerable<T> regardless of the actual runtime type (in my opinion, it could just have casted the argument to a List<T> if it already was one).

This obviously has some performance overhead, especially for large list (or lists of large objects).

So what can I do to overcome this problem, and why is there no ForEach method in Linq?

By the way, his question is vaguely related to this one.

+2  A: 

You can write your own extension method for IEnumerable<T> like so:

    public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
    {
        foreach (T t in enumerable)
            action(t);
    } 

Such a method is not present in Linq, because Linq is basically meant for queries, not for simple iterations.

Also, note that when using an actual List<T> instance, the extension method will not be called because instance methods have precedence over extension methods when they share signature.

The following code, for example, would not invoke the extension method:

 var l = new List<MyClass>();
 l.Add(new MyClass());
 l.ForEach(DoSomething);

Whereas the following would:

IEnumerable<MyClass> l = new List<MyClass>(new []{new MyClass()});
l.ForEach(DoSomething);  
klausbyskov
Honestly, I really dislike this. It's not wrong, so I'm not going to downvote it, but you're conflating functional (closure) and imperative semantics here and it can easily lead to unexpected, hard-to-debug problems or at best poor readability.
Aaronaught
then down vote it Aaronaught
Brian Leahy
+5  A: 

You can write an extension method but there are good reasons why ForEach is not implemented on IEnumerable<T>. The second example

result.ToList().ForEach(DoSomething);

copies the IEnumerable into a List (unless it's already a List, I assume) so you're better off just iterating the IEnumerable with good old foreach(var r in result) {}.

Addendum:

For me, the key point of Eric Lippert's article is that adding ForEach has no benefit and adds some potential pitfalls:

The second reason is that doing so adds zero new representational power to the language. Doing this lets you rewrite this perfectly clear code:

foreach(Foo foo in foos){ statement involving foo; }

into this code:

foos.ForEach((Foo foo)=>{ statement involving foo; });

which uses almost exactly the same characters in slightly different order. And yet the second version is harder to understand, harder to debug, and introduces closure semantics, thereby potentially changing object lifetimes in subtle ways.

Jamie Ide
+1 for linking to Eric Lippert's definitive post on why there is no ForEach in Linq
Gabe Moothart
That is correct, LINQ was designed for querying, not for iteration
masenkablast
I agree with the Eric's analysis of his example, but I think he neglects this: `foos.ForEach(SomeMethod)`. There are no closure semantics to speak of, and in my opinion it's actually easier to read and more concise than a `foreach` loop when all you want to do is call a method for each item in a list. Because of the clear benefit and lack of significant drawbacks, I went ahead and implemented my own extension method, and I'll never give it up!
Joel Mueller
+5  A: 

I would prefer this:-

foreach (var item in result.ToList())
{
   DoSomething(item);
}

Its a much clearer idiom, it says collect a list of stuff together then do something important that may change the state of the application. Its old school but it works and is actually more understandable to a wider audience.

AnthonyWJones
And easier to debug, IMHO.
TheSoftwareJedi
I agree with the foreach but why convert to a List if it's already IEnumerable?
Jamie Ide
@Jamie: The `ToList` is there because its in the original question. There are times when that is necessary. If for example the `DoSomething` represents an operation that removes items from a collection that is the ultimate source of the `IEnumerable` then you'd likely get an exception if you don't include the `ToList`. Of course if you are happy that `DoSomething` isn't going to do that then most likely it isn't necessary.
AnthonyWJones
You can also put a breakpoint on the `ForEach` delegate parameter, so debugging is as easy in either case.
RedGlyph
@RedGlyph: Good point, I've removed Brian's edit of my answer. Am I off message to be slightly irritated with someone adding content like that to my answer?
AnthonyWJones
+3  A: 

I use 2 methods. One iterates the list, one works with lazy eval. I use them as the situation defines.

    public static IEnumerable<T> ForEachChained<T>(this IEnumerable<T> source, Action<T> action)
    {
        foreach (var item in source)
        {
            action(item);
            yield return item;
        }
    }

    public static IEnumerable<T> ForEachImmediate<T>(this IEnumerable<T> source, Action<T> action)
    {
        foreach (var item in source)
        {
            action(item);
        }
        return source;
    }
TheSoftwareJedi
A: 

If you decide to do this via an extension method, I'd call it ForAll instead of ForEach. This in order to use the same syntax as the Parallel Extensions in .NET 4.0 use:

var result = // web service call
result.AsParallel().ForAll(DoSomething);
CodingInsomnia
Why call it `ForAll`, introducing confusion with the parallelized version, when there's already an existing Framework method `ForEach` with this exact semantics (just a different type)?
Pavel Minaev
+1  A: 

You can write your own extension method ToList(this List theList){return theList;} and then avoid the overhead. Since your extension method is the most specific one it will be called, not the one on IEnumerable

AndreasKnudsen
Good point ....
klausbyskov