ForEach is regularly proposed as a missing feature in the BCL but has been rejected in the past apparently because it wouldn't be "functional" enough, doesn't return anything so calls cannot be chained together in a LINQ-like pipeline, and so on. So here's a different approach that is undeniably functional, and does support chaining together.
publicstatic IEnumerable<Action> ForEach<T>(this IEnumerable<T> source, Action<T> action) { foreach (T item in source) { T captured = item; yield return () => action(captured); } }
That extension method turns a list of items of any type into a list of actions, by binding a dedicated action to each item. The resulting list can then be operated on further - e.g. interleaved with other lists, reversed, counted, and so on.
Another useful operation is a specialised form of Aggregate that turns a list of actions into a single action. The CLR already has an efficient representation of this: a multicast delegate, so that's what I prepare and return:
publicstatic Action Aggregate(this IEnumerable<Action> actions) { Action del = null; foreach (Action action in actions) del += action; return del; }
So now I can write things like:
int[] arr = { 3, 1, 4 };
Action act = arr.ForEach(Console.WriteLine)
.Concat(arr.ForEach(Console.WriteLine).Reverse())
.Aggregate();
act();
(Oh, how much clearer that would look with operator overloading through extension methods, using + to concatenate the lists, but I digress...)
Which prints:
3 1 4 4 1 3
So the bound actions generated from the lists can be chained, reversed, rechained, signed in triplicate and finally buried for three months and then recycled as firelighters.
By the way, the feedback from MS on the plain ForEach suggestion is baffling in any case. They don't want to support stateful, imperative programming? Then they need to remove quite a lot of existing features from the language, such as assignment! By all means support functional programming smoothly with C# (a brilliant strategy) but don't get confused into thinking that you're maintaining F#. The whole point of C# is to be the number 1 language for building and using libraries in the CLR, and such libraries are expressed through components that are, for better or worse, brimming with mutable state.
Meanwhile, you can add features for specifying whether a given data structure is mutable or not, and this will take control of naughty mutation far more effectively than merely failing to add frequently useful features to the BCL.