The advice of Eric Lippert is not to write such loops as expressions.
Only use query expressions if the code does not have side-effects and produces a value.
In this case, you're looping to repeat a statement, which has a side-effect on the console and doesn't return values. So a foreach loop is clearer and is designed specifically for this purpose.
On the other hand, an action (which may have side-effects) can be regarded as a pure value before it is executed. So here's a list of numbers:
List<int> numbers = Enumerable.Range(1, 10).ToList();
From that we make a list of actions:
List<Action> actions = numbers.Select(n => Console.WriteLine(n)).ToList();
Although we're dealing with actions that have side effects, we aren't actually running them at all, so any further manipulations on the content of that list are not side-effecting. Then finally when we have the list we need, we can use a forloop to execute it:
foreach (var a in actions)
a();
And that is such a simple pattern, it could be argued that a RunAll
extension method on IEnumerable<Action>
would be no bad thing. Indeed, the .NET framework has this concept built into it: a multicast delegate is a single thing you can call which executes a bunch of delegates on a list. In the most common use cases (events), those delegates have side-effects.