List<T>.ForEach vs. custom IEnumerable<T> extension
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&开发者_C百科lt;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.
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.
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.
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;
}
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);
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
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);
精彩评论