78

Usually we do something like a for or while loop with a counter:

for (int i = 0; i < 10; i++)
{
    list.Add(GetRandomItem());
}

but sometimes you mix up with boundaries. You could use a while loop instead, but if you make a mistake this loop is infinite...

In Perl for example I would use the more obvious

for(1..10){
    list->add(getRandomItem());
}

Is there something like doitXtimes(10){...}?

mbx
  • 6,292
  • 6
  • 58
  • 91

7 Answers7

96

Well you can easily write your own extension method:

public static void Times(this int count, Action action)
{
    for (int i = 0; i < count; i++)
    {
        action();
    }
}

Then you can write:

10.Times(() => list.Add(GetRandomItem()));

I'm not sure I'd actually suggest that you do that, but it's an option. I don't believe there's anything like that in the framework, although you can use Enumerable.Range or Enumerable.Repeat to create a lazy sequence of an appropriate length, which can be useful in some situations.


As of C# 6, you can still access a static method conveniently without creating an extension method, using a using static directive to import it. For example:

// Normally in a namespace, of course.
public class LoopUtilities
{
    public static void Repeat(int count, Action action)
    {
        for (int i = 0; i < count; i++)
        {
            action();
        }
    }
}

Then when you want to use it:

using static LoopUtilities;

// Class declaration etc, then:
Repeat(5, () => Console.WriteLine("Hello."));
ToolmakerSteve
  • 18,547
  • 14
  • 94
  • 196
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 4
    Would be nice to have some syntax for that without the extra "() =>" =) – Massimiliano Oct 14 '10 at 11:03
  • 6
    @Yacoder: No, it wouldn’t, because then the meaning of the code would be ambiguous. – Timwi Oct 14 '10 at 11:08
  • 1
    @Timwi: I think Yacoder means a completely different syntax, maybe using blocks as in Ruby for instance. Not just writing `10.Times(list.Add(GetRandomItem()));`, which indeed would be very annoying and ambiguous (for both humans and compilers). – haylem Oct 14 '10 at 11:14
  • @Yacoder: If @haylem is right, well then any syntax you can come up with is just a replacement for `() =>`, which I think is already short enough. – Timwi Oct 14 '10 at 11:22
  • @Yacoder: Well, in this case you could use `10.Times(delegate{ list.Add(GetRandomItem()); });` but that's even more muddled, imo. – LukeH Oct 14 '10 at 11:22
  • You just need to express that you're not calling this method on list, but rather passing this call as a lambda... maybe something like `list -> Add(GetRandomItem())`... don't know, it's just an idea, which could make this code better readable (hopefully) – Massimiliano Oct 14 '10 at 11:44
  • The Enumerable .Repeat works perfectly for what I needed here. I would presume it is more efficient than generating this number of entries. – Schroedingers Cat Feb 13 '15 at 10:15
  • Sincerely hope that one day developers of the world would stop encouraging use of `i++` instead of `++i` for loop variable :) – Grozz Sep 03 '16 at 12:20
  • 1
    @Grozz: Why? I don't know about you, but I certainly find `i++` more readable, and I would expect them to be entirely equivalent in performance... – Jon Skeet Sep 03 '16 at 12:23
  • @JonSkeet: there's a feeling of it being more readable because we've seen it more and thus we repeat it and continue the vicious circle. `i++` reads as "increment value and return the previous" (why this extremely confusing operation was ever considered ok?), `++i` reads "increment value and use it", there's no reason besides custom to use the former. You are basically doing 3 things instead of 2 and every time somebody reads this out of loop context (which again is only ok because we are so used to it) it creates additional mental burden, i.e. "why postfix? what's the use for previous value?" – Grozz Sep 03 '16 at 22:39
  • 1
    @Grozz: Except that we're not using the value, so it doesn't matter... So I don't see any additional mental burden... But you're the one suggesting that the rest of the world should conform to your idea of what's better, which adds more burden for them. Has anyone ever actually told you that they *do* find there's more burden? You seem to be assuming it. – Jon Skeet Sep 04 '16 at 06:39
  • @JonSkeet: I'm with you here, we're not using the value, so why are we using the thing that does more than what's necessary? Of course, I would only have an anecdotal evidence of one form being more burden than the other, but I insist that's only because the "less correct" one is more popular and is being forced onto new developers. Anyway, our entire discussion seems to be the explanation why so many languages do not allow either form, which I think is even better. – Grozz Sep 04 '16 at 11:29
  • 7
    I suggest meeting in the middle with the following compromise: +i+. The operation would increment half the value before the operation and half after the operation. This way, both parties can be satisfied. – Greg Feb 22 '17 at 19:39
  • @ToolmakerSteve Did you see where I said I don't think I'd actually suggest doing this? I gave it as an option, that's all. – Jon Skeet Dec 22 '17 at 00:50
  • @ToolmakerSteve I wouldn't suggest either, to be honest - not in general. Maybe for very specific situations, but usually I'd go with a simple loop instead. Note that before static imports, unless you included this method in the class using it, you'd have to use Foo.Repeat or whatever, rather than the OP's suggested single-identifier option. (The answer was written long before C# 6 of course.) – Jon Skeet Dec 22 '17 at 00:56
  • I've updated your answer to include a better syntax for this method, via C# 6's `using static` as you suggest. Thanks - that is what I was searching for. (I've deleted my comments above.) – ToolmakerSteve Dec 23 '17 at 14:50
  • @ToolmakerSteve: Please don't make such huge edits in the future. That would have been much better as a new answer. – Jon Skeet Dec 23 '17 at 15:30
  • OK. I'll make it a new answer, then delete it here. – ToolmakerSteve Dec 23 '17 at 15:36
  • @Toolmaker: I've edited it now so it looks more like something I'd write, but I'd suggest avoiding large edits in the future. – Jon Skeet Dec 23 '17 at 15:37
  • OK, given SOs vote-based format, I should have merely put a *comment* that this was a good answer at the time it was written, but when using C# 6, `using static` makes an alternative syntax possible - and added such an answer, as you suggest. – ToolmakerSteve Dec 23 '17 at 15:58
73
foreach (var i in Enumerable.Range(0, N))
{
    // do something
}
Grozz
  • 8,317
  • 4
  • 38
  • 53
  • 2
    This version is nice, when you need the index. – mbx Oct 14 '10 at 13:14
  • 1
    Succinct and works - the index can always be ignored if it's not needed – Christopher J Smith Jun 26 '14 at 19:58
  • 2
    This is the one I'm going with, since it provides a succinct but clear way to do the iteration. I feel like it's way more compact than Paul's answer, although they're really the same solution. – KGVT Aug 31 '16 at 20:40
  • @KGVT - I agree with you. What makes this pleasing is not its "compactness", which is not that different than the original code or Paul's, but its "readability". Its "declarative" like Paul's, but in a more familiar layout. – ToolmakerSteve Dec 22 '17 at 01:07
  • 1
    If you don't need the counter (`i`), C# 7 now allows you to use [discards](https://learn.microsoft.com/en-us/dotnet/csharp/discards): `foreach (var _ in Enumerable.Range(0, N))` – Gabriel Luci Jan 31 '19 at 18:44
  • have to use `using System.Linq;` to use `Enumerable` – cikatomo Jan 22 '23 at 15:31
40

One can create an IEnumerable of Int32:

Enumerable.Range(0, 10);

The ForEach extension method is also widely known (although not shipped with .NET). You could combine the two:

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

So your example would become:

Enumerable.Range(0, 10).ForEach(_ => list.Add(GetRandomItem()));
Community
  • 1
  • 1
Paul Ruane
  • 37,459
  • 12
  • 63
  • 82
  • @mbx: Yes, Jon Skeet's Times method is much better when the index is not required and you just want to repeat an operation. – Paul Ruane Oct 14 '10 at 13:16
  • 3
    BTW I don't have ForEach without ToList function Enumerable.Range(0, 10).ToList().ForEach(i => sb.AppendLine($"{i}")); They should do some dedicated lambda for this. – oobe May 04 '20 at 08:14
16

I see Jon Skeet beat me to it, but this variation will allow you to pass the index to the Action each time it is run:

public static class IntegerExtensions
{
  public static void TimesWithIndex(this int count, Action<int> action)
  {
     for (int i = 0; i < count; i++)
        action(i);
  }
}

And call it like this:

10.TimesWithIndex((i) =>
            obj[i].DoSomething());
Community
  • 1
  • 1
Alex Baranosky
  • 48,865
  • 44
  • 102
  • 150
8
while (i-- > 0) {

}

You mentioned that while loop is dangerous as it may be infinite - the above form is pretty simple and will never be infinite. At least TOTALLY infinite :)

It's convenient and short (shorter than any of the other answers) and will run exactly i times (because postfix decrement returns value before decrementation).

Vimes
  • 10,577
  • 17
  • 66
  • 86
aaimnr
  • 1,646
  • 1
  • 17
  • 31
  • 5
    You missed the variable declaration and initialization which would then render your version inferior to the requested `for(1..10)` and the selected answer of `int.Times` via the extension method. – mbx Oct 19 '17 at 05:35
  • Indeed, the best use case for this scenario is when you declare/initialise the loop count in previous instructions using some more involved logic and here just apply the loop logic. – aaimnr Oct 19 '17 at 09:08
5

There's still one way missing:

List<T> list = System.Linq.Enumerable.Range(0, 10).Select(_ => GetRandomItem()).ToList();

Where T is the type returned by GetRandomItem()

A. Morel
  • 9,210
  • 4
  • 56
  • 45
  • Listing your first "solution" here is actually dangerous as it does not solve the *calling random repeatedly* problem - It calls `GetRandomItem()` only once. Unless you have a [very strange understanding of randomness](https://xkcd.com/221/) the calls should return at least two different values for a big enough set of calls. The second variant looks legit though. [Both of your solutions in comparison](https://dotnetfiddle.net/oGOf5x). – mbx Sep 12 '18 at 12:48
  • 2
    Right, I just edited the post to rectify my mistake. Thx – A. Morel Sep 12 '18 at 14:39
3

Example 1

            var loop = new Loop(50);
            foreach(var index loop) {
                // do something
            }

Example 2

            foreach(var index in 50.Times().Start(1).Step(-1)) {
                // do something
            }

Example 3

            var loop = 20.Times();
            while (loop.Do) {
                // do something
            }

Loop class & extension

public class Loop : IEnumerable<int> {

    readonly int times = 0;
    int start = 0;
    int step = 1;
    IEnumerator<int> e;

    public Loop (int times, int start = 0, int step = 1) {
        this.times = times < 0? 0-times : times;
        this.start = start;
        this.step = step;
    }

    public Loop Start(int value) {
        this.start = value;
        return this;
    }

    public Loop Step(int value) {
        this.step = value;
        return this;
    }

    public bool Do {
        get {
            if (this.e.IsNull()) {
                this.e = this.GetEnumerator();
            }
            if (this.e.MoveNext()) {
                return true;
            }
            else {
                this.e.Dispose();
                this.e = null;
                return false;
            }
        }
    }

    IEnumerator IEnumerable.GetEnumerator() {
        return this.GetEnumerator();
    }
    public IEnumerator<int> GetEnumerator() {
        int count = times;
        int value = start;
        while (count != 0) {
            yield return value;
            try {
                value += step;
            }
            catch (OverflowException) {
                break;
            }
            --count;
        }
        yield break;
    }
}

public static class IntLoopExtension {

    public static Loop Times (this int self) {
        return new Loop (self);
    }

}
ddur
  • 75
  • 3