55

In C#, using .NET Framework 4, is there an elegant way to repeat the same action a determined number of times? For example, instead of:

int repeat = 10;
for (int i = 0; i < repeat; i++)
{
    Console.WriteLine("Hello World.");
    this.DoSomeStuff();
}

I would like to write something like:

Action toRepeat = () =>
{
    Console.WriteLine("Hello World.");
    this.DoSomeStuff();
};

toRepeat.Repeat(10);

or:

Enumerable.Repeat(10, () =>
{
    Console.WriteLine("Hello World.");
    this.DoSomeStuff();
});

I know I can create my own extension method for the first example, but isn't there an existent feature which makes it already possible to do this?

Arseni Mourzenko
  • 50,338
  • 35
  • 112
  • 199

11 Answers11

62

Like this?

using System.Linq;

Enumerable.Range(0, 10).ForEach(arg => toRepeat());

This will execute your method 10 times.

[Edit]

I am so used to having ForEach extension method on Enumerable, that I forgot it is not part of FCL.

public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
    foreach (var item in source)
        action(item);
}

Here is what you can do without ForEach extension method:

Enumerable.Range(0, 10).ToList().ForEach(arg => toRepeat());

[Edit]

I think that the most elegant solution is to implement reusable method:

public static void RepeatAction(int repeatCount, Action action)
{
    for (int i = 0; i < repeatCount; i++)
        action();
}

Usage:

RepeatAction(10, () => { Console.WriteLine("Hello World."); });
Kirill Kobelev
  • 10,252
  • 6
  • 30
  • 51
Alex Aza
  • 76,499
  • 26
  • 155
  • 134
  • 5
    An old answer, but you can also do `Enumerable.Range(0, 10).Select(o => /* return or do something */)` – will-hart Aug 19 '16 at 23:38
  • 1
    @will-hart That's dirty and not very clear on first sight what you are doing. Writing an extension method is better than abusing select for doing an action. – Wolfsblvt Aug 31 '17 at 08:38
  • "I am so used to having ForEach extension method on Enumerable" I have the same problem when I try to post code; too many useful methods that don't normally exist. – Dave Cousineau Aug 18 '18 at 21:47
29

There is no built-in way to do this.

The reason is that C# as it is tries to enforce a divide between the functional and imperative sides of the language. C# only makes it easy to do functional programming when it is not going to produce side effects. Thus you get collection-manipulation methods like LINQ's Where, Select, etc., but you do not get ForEach.1

In a similar way, what you are trying to do here is find some functional way of expressing what is essentially an imperative action. Although C# gives you the tools to do this, it does not try to make it easy for you, as doing so makes your code unclear and non-idiomatic.

1 There is a List<T>.ForEach, but not an IEnumerable<T>.ForEach. I would say the existence of List<T>.ForEach is a historical artifact stemming from the framework designers not having thought through these issues around the time of .NET 2.0; the need for a clear division only became apparent in 3.0.

Domenic
  • 110,262
  • 41
  • 219
  • 271
  • 5
    Of course, nothing stops you from writing your own side-effect-ridden `Repeat` or `ForEach` method on IEnumerable - the language designers have washed their hands by that point. – geofftnz Jun 20 '11 at 04:38
  • 2
    Can I ask why using the `ForEach` as an example, is such a bad thing? – Paul C Jun 21 '13 at 15:34
  • 2
    @CodeBlend, that would make a great question. – Joe Apr 20 '15 at 23:01
  • 1
    I believe sometimes you're better off writing a for-loop performance wise. An `Enumerable` should do what it implies; enumerate. It's good that the designers made a clear difference between manipulable collections and iterateable collections to avoid confusion – DerpyNerd Aug 04 '16 at 12:02
  • 2
    @Domenic - I think your explanation is correct for why LINQ doesn't help with this. However, it doesn't apply to the broader question of why C# lacks an *imperative* way to say "repeat a loop n times". Other languages do so. Or at least get closer to doing so, like VB's `For .. To ..`, which avoids the semantic overkill of C#'s testing and incrementing or decrementing a loop variable, in the common case of stepping through a range of values. We are stuck with a syntax that was invented for C in 1970s, because it was efficient on a DEC PDP-11, which had increment and decrement instructions. – ToolmakerSteve Dec 22 '17 at 01:57
  • Given that "side effects are bad" and "`ForEach` is bad because it has side effects", what is the alternative? What is the "pure" alternative to performing an action on each item in a sequence? – Dave Cousineau Aug 18 '18 at 22:03
  • Yes there is a built-in way to do exactly this, see Ali's answer below `Enumerable.Repeat(() => f() ,10);` – kofifus Nov 19 '18 at 01:06
  • The link is dead :( – Zero3 Apr 04 '20 at 15:27
9

For brevity of a one liner you could do this. Not sure what you think...

Enumerable.Repeat<Action>(() => 
{
    Console.WriteLine("Hello World.");
    this.DoSomeStuff();
}, 10).ToList().ForEach(x => x());
Aron
  • 99
  • 1
  • 1
  • 5
    o think this is not elegant. This is less readable and harder to understand than a simple for statement. – Ewerton Oct 26 '12 at 17:02
  • @Ewerton perhaps that's why they left this feature out; more syntax cluttering than using a simple foreach. – Oyvind Jul 29 '19 at 10:02
8

Without rolling out your own extension, I guess you can do something like this

    Action toRepeat = () => {
        Console.WriteLine("Hello World.");
         this.DoSomeStuff();
    };

    int repeat = 10;
    Enumerable.Range(0, repeat).ToList().ForEach(i => toRepeat());
Bala R
  • 107,317
  • 23
  • 199
  • 210
7
Enumerable.Repeat<Action>(() => { Console.WriteLine("Hello World"); this.DoSomething(); },10).ToList().ForEach(f => f.Invoke());

elegant isn't it?

Ali Humayun
  • 1,756
  • 1
  • 15
  • 12
5

Most convenient I find is:

Enumerable.Range(0, 10).Select(_ => action());
M. Christopher
  • 305
  • 3
  • 14
3

I wrote it as an Int32 extension method. At first I thought maybe that doesn't sound like a good idea, but it actually seems really great when you use it.

public static void Repeat(
   this int count,
   Action action
) {
   for (int x = 0; x < count; x += 1)
      action();
}

Usage:

5.Repeat(DoThing);

value.Repeat(() => queue.Enqueue(someItem));

(value1 - value2).Repeat(() => {
   // complex implementation
});

Note that it will do nothing for values <= 0. At first I was going to throw for negatives, but then I'd always have to check which number is greater when comparing two. This allows a difference to work if positive and do nothing otherwise.

You could write an Abs extension method (either value.Abs().Repeat(() => { }); or -5.RepeatAbs(() => { });) if you wanted to repeat regardless of sign and then the order of the difference of two numbers wouldn't matter.

Other variants are possible as well, like passing the index, or projecting into values similar to Select.

I know there is Enumerable.Range but this is a lot more concise.

Dave Cousineau
  • 12,154
  • 8
  • 64
  • 80
1
Table table = frame.AddTable();
int columnsCount = 7;

Enumerable.Repeat<Func<Column>>(table.AddColumn, columnsCount)
          .ToList()
          .ForEach(addColumn => addColumn());
//or
Enumerable.Range(0, columnsCount)
          .ToList()
          .ForEach(iteration => table.AddColumn());

these options are not elegant because of ToList(), but both worked in my case

Oleg
  • 350
  • 4
  • 6
1

Declare an extension:

public static void Repeat(this Action action, int times){
    while (times-- > 0)
        action.Invoke();
}

You can use the extension method as:

        new Action(() =>
                   {
                       Console.WriteLine("Hello World.");
                       this.DoSomeStuff();
                   }).Repeat(10);
0

If your action is thread-safe you could use

Parallel.For(0, 10, i => myAction(schema));
Russell Horwood
  • 1,015
  • 1
  • 11
  • 19
0

This repeats twice, there is also a iteration variable for your convenience:

foreach (var iteration in Enumerable.Range(0, 2))
{
     // do stuff
}
Wizzard
  • 229
  • 3
  • 7