Is there a shorter/simpler version of the for loop to anything x times?
C#.NetC# Problem Overview
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){...}
?
C# Solutions
Solution 1 - C#
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."));
Solution 2 - C#
foreach (var i in Enumerable.Range(0, N))
{
// do something
}
Solution 3 - C#
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()));
Solution 4 - C#
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());
Solution 5 - C#
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).
Solution 6 - C#
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()
Solution 7 - C#
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);
}
}