Is there any method like ForEach for IList?
Possible Duplicate:
LINQ equivalent of foreach for IEnumerable<T>
List<T>
has a method called ForEach
which executes the passed action on each element of it.
var names = new List<String>{ "Bruce", "Alfred", "Tim", "Richard" };
names.ForEach(p => { Console.WriteLine(p); });
But what if names
is not a List<T>
but an IList<T>
, IList<T>
doesn't have a method like ForEach
. Is there some alternative?
Use a foreach
loop:
foreach (var p in names) {
Console.WriteLine(p);
}
There is no reason to use delegates and extension methods all over the place if that doesn't actually improve readability; a foreach
loop is not any less explicitly telling readers what's being done than a ForEach
method.
If your IList<T>
is an array ( T[]
), then you have Array.ForEach method on them similar to ForEach
on List<T>
. You can create an extension method for your custom IList<T>
or IEnumerable<T>
or whatever you prefer.
public static void ForEach<T>(this IList<T> list, Action<T> action)
{
foreach (T t in list)
action(t);
}
You just have to be wary of the fact that the objects in the original collection will be modified, but I guess the naming does imply that.
------------------------------------------------------------------------------------------------------------------------------------
I prefer to call:
people.Where(p => p.Tenure > 5)
.Select(p => p.Nationality)
.ForEach(n => AssignCitizenShip(n);
than
foreach (var n in people.Where(p => p.Tenure > 5).Select(p => p.Nationality))
{
AssignCitizenShip(n);
}
If so you can create the extension method on IEnumerable
. Mind you the terminating call ForEach
executes the Linq
query. If you do not want it, you can defer it too by using yield
statement and returning an IEnumerable<T>
back:
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> list, Action<T> action)
{
foreach (T t in list)
{
action(t);
yield return t;
}
}
That solves the side-effect issue, but I personally like a method named ForEach
to finally execute the call.
-----------------------------------------------------------------------------------------------------------------------------------
To address the opposing views on preferences, here is a better link from Eric Lippert than this. To quote him:
"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".
Eric's not saying it's a bad thing to do - just the philosophical reasons behind the decision to not include the construct in Linq
by default. If you believe a function on an IEnumerable
shouldn't act on the contents, then don't do it. Personally I dont mind it since I'm well aware what it does. I treat it as any other method that causes side-effect on a collection class. I can enter into the function and debug it too if I want. Here is another one from Linq
itself.
people.Where(p => p.Tenure > 5)
.Select(p => p.Nationality)
.AsParallel()
.ForAll(n => AssignCitizenShip(n);
As I would say, there is nothing bad about these. Its just personal preference. I wouldn't use this for nested foreach
s or if it involves more than one line of code to execute inside the foreach
loop since thats plain unreadable. But for simple example I posted, I like it. Looks clean and concise.
Edit: See a performance link btw: Why is List<T>.ForEach faster than standard foreach?
You could make an extension method and use most of the implementation of void List<T>.ForEach(Action<T> action)
. You can download the source code at the Shared Source Initiative site.
Basically you will end to something like this:
public static void ForEach<T>(this IList<T> list, Action<T> action)
{
if (list == null) throw new ArgumentNullException("null");
if (action == null) throw new ArgumentNullException("action");
for (int i = 0; i < list.Count; i++)
{
action(list[i]);
}
}
It is slightly better than the other implementations that use the foreach
statement since it takes advantage of the fact that IList includes an indexer.
Although I aggree with the answer of OR Mapper, sometimes in big projects with many developers it is hard to convicne everybody that a foreach
statement is clearer. Even worse, if your API is based on interfaces (IList) instead of concrete types (List) then developers that are used to the List<T>.ForEach method
might start calling ToList
on your IList references! I know because it happened in my previous project. I was using the collection interfaces everywhere in our public APIs following the Framework Design Guidelines. It took me a while to notice that many developers where not used to this and call to ToList
started apprearing with an alarming rate. Finally I added this extension method to a common assembly that everybody was using and made sure that all unecessary call to ToList
were removed from the codebase.
上一篇: 在列表<>上选择和ForEach