When I read this answer, which has code:
linqObject.Where((obj, index) => {
DoWork(obj, index);
return true;
}).ToArray(); //MUST CALL ToArray() or ToList() or something to execute the lazy query, or the loop won't actually execute
My initial reaction was similar to Mark Sowul's comment:
"Wastes memory (allocating a list or array) and risks bugs (if you don't call ToList or ToArray). Why?".
But on further thought, what is a concise approach, avoiding the downsides of the above?
My first attempt:
int index = 0;
foreach (var item in someEnumerable) {
DoWork(item, index);
index++;
}
Compared to calling a Linq operator, that seems verbose, low-level. I'm defining and manipulating an index, whereas Linq has an Operator that "almost" does what I want - takes care of the index tracking for me.
Personally, I would write the explicit foreach
loop - but maybe that is only because I've been programming a long time, and am comfortable with that syntax.
Is there a more concise solution, that doesn't have the downsides of the first version shown (Where / ToArray)?
I'm trying to get comfortable with encapsulating such stuff into methods, often by using Action<>
or Func<>
, which are newer [to me] than the style of programming that became my habit. So that instead of repeating similar snippets of code, I just call a method.
As an aside, Linq is also newer than my habits. Linq is concise, but not in a way that I find helpful; I find loops clearer to read; a better separation of the aspects of what is happening. But I'm open to seeing situations where its expressive power is appropriate.
[OFF-TOPIC: In contrast, I spent some time with F#, and found it to be fantastic. I LOVE the combination of functional with OO; just not the way Linq jams a handful of functional constructs into C#'s syntax. But this question is about making good use of C#, so all advice is appreciated.]
I think this becomes:
public static void ForEachWithIndex<T>(IEnumerable<T> e, Action<int, T> a)
{
int index = 0;
foreach (T v in e) {
a(index, v);
index++;
}
}
which might be used like this:
ForEachWithIndex(e, (int index, T item) => {
// Do Work based on index and item.
});
Or if an appropriate "DoWork" method already exists, simply:
ForEachWithIndex(e, DoWork);
Comments? Improvements, or alternatives?
Is there a way to use Linq more concisely/efficiently than in the original code sample?
NOTE: I did not make this an "extension method" on "IEnumerable", due to comments people made in stackoverflow threads regarding "ForEach" as a possible extension method: dislike of a syntax that looks similar to a Linq operator - which traditionally don't have side-effects - for a method that clearly DOES have side-effects; indeed is all about the side-effects.