EDIT Additional options and a slightly extended question below.
Consider this contrived and abstract example of a class body. It demonstrates four different w
With the exception of (C), which seems to read backwards to me, I can think of situations in which you might want to use each of the others. In addition, depending on what you are doing you could also throw in standard LINQ into the mix. For example, if your loop simply uses the list item to create some other object.
(E) var someOtherCollection = someList.Select( l => transform(l) );
For option (A), if you need to know the position in the list as well as the using the item. Option (B) or (E) would be what I would typically use. Option (D) makes sense if the list is large and the actions are amenable to being parallelized (no or manageable dependencies between the items).
Since you're using a generic list all but (E) are O(N). Count() should be an O(1) operation as it is kept internally in a variable. On other enumerable types, you'd need to know how the data structure is constructed. If you don't know the type of the collection, I'd use the foreach
implementation or LINQ over the indexed implementation since the collection may not be indexed and that could turn your enumeration into a O(N2) operation.
Option A only really makes sense for sequences that implement indexing and will only be performant for those that have O(1)
lookup time. Generally, I would use the foreach
and variants unless you have special logic.
Also note, that "special logic" like for (int i = 1; i < list.Count; i++)
can be implemented with Linq extension methods: foreach(var item in sequence.Skip(1))
.
So, generally prefer B over A.
As to C: This can be confusing for other developers if they aren't used to the functional style.
As to D: This will depend on a lot of factors. I guess for simple calculations, you don't want to do this - you will only really benefit from parallelization if the loop body takes a while to compute.
you missed:
Parallel.ForEach(someList, o => o.someAction())
Parallel.For(0, someList.Length, i => someList[i].someAction())
for(int i = 0...)
To use this methodology you must have an array that you can access each element of, one by one.
foreach (SomeClass o in someList)
This syntax can be used on an enumerable class, a class that implements IEnumerable
. IEnumerable has a method GetEnumerator()
that knows how to go through each element of the collection. Now, the array above DOES implement IEnumerable
. The way it knows how to enumerate through the collection is how you defined it above. However, not all IEnumerable
classes that can use the foreach syntax can use the first method, as not all collections provide access to each element. Consider the following function (didn't test it):
public IEnumerable<int> GetACoupleOfInts()
{
yield return 1;
yield return 2;
}
}
This method will allow you to use the foreach
construct, as the runtime knows how to enumerate through the values of GetACoupleInts()
, but would not allow the for
construct.
someList.ForEach(o => o.someAction());
- The way I understand it, this lambda will just be converted to the same expression as foreach (SomeClass o in someList)
someList.AsParallel().ForAll(o => o.someAction());
- When deciding whether or not to use PLINQ you have to decide whether or not the "Juice is worth the squeeze." If the amount of work in someAction()
is trivial, then the overhead of the runtime trying to organize all of the data from the concurrent actions will be WAY too much and you would be better off doing it serially.
tl;dr - The first three will likely result in the same calls and have no real affect on performance, although they have different meanings within the framework. The fourth option needs more consideration before being used.
As far as performance is concerned I think one of these would work best.
//A. for
for (int i = 0; i < someList.Count(); i++)
{
someList[i].someAction();
}
or
//D. plinq
someList.AsParallel().ForAll(o => o.someAction());
Although in case of A, I would prefer not to do someList.Count() every time.
for
performs better as compared to foreach
as far as performance is concerned. D can be better than A but it would depend on the scenario. If you have some large data in somelist, Parallelism might help but if you have small data, it can cause extra burden
Generally I go with what logically matches what I'm doing. If I'm looping over the entire list all use foreach but if I'm looping through a subset I use a for loop. Also, if you are modifying the collection in your loop you have to use a for loop.
The only other option that I'm aware that hasn't already been stated is to manually do what foreach is doing, which is useful if you need to maintain the state of the enumerator outside of the scope in which its created.
using(var myEnum = aList.GetEnumerator()){
while(myEnum.MoveNext()){
myEnum.Current.SomeAction();
}
}