2010-10-14 7 views
37

Por lo general, hacer algo como un favor o bucle while con un contador:¿Hay alguna versión más corta/más simple del ciclo for para algo x veces?

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

pero a veces se mezcla con límites. Se puede usar un bucle while en su lugar, pero si se comete un error de este bucle es infinito ...

En Perl, por ejemplo, me gustaría utilizar la más obvia

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

¿Hay algo así como "doitXtimes (10) {...} "?

Respuesta

52

bien que se puede escribir fácilmente su propio método de extensión:

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

entonces usted puede escribir:

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

No estoy seguro de que en realidad te sugiero que hagas que, pero es una opción. No creo que haya nada de eso en el marco, aunque puede usar Enumerable.Range o Enumerable.Repeat para crear una secuencia diferida de una longitud adecuada, que puede ser útil en algunas situaciones.


A partir de C# 6, todavía se puede acceder a un método estático convenientemente sin necesidad de crear un método de extensión, utilizando una directiva using static importarlo. Por ejemplo:

// 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(); 
     } 
    } 
} 

Entonces, cuando usted quiere usarlo:

using static LoopUtilities; 

// Class declaration etc, then: 
Repeat(5,() => Console.WriteLine("Hello.")); 
+3

Sería bueno tener alguna sintaxis para eso sin el extra "() =>" =) –

+2

@Yacoder: No, no lo haría, porque entonces el significado del código sería ambiguo. – Timwi

+0

@Timwi: creo que Yacoder significa una sintaxis completamente diferente, tal vez usando bloques como en Ruby, por ejemplo. No solo escribir '10.Times (list.Add (GetRandomItem()));', que de hecho sería muy molesto y ambiguo (tanto para humanos como para compiladores). – haylem

26

Uno puede crear un IEnumerable de Int32:

Enumerable.Range(0, 10); 

El método ForEach extensión también se conoce ampliamente (aunque no enviado con .NET).Se puede combinar los dos:

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

Así que su ejemplo se convertiría en:

Enumerable.Range(0, 10).ForEach(_ => list.Add(GetRandomItem())); 
+0

Aunque esto funciona, no es muy útil. – mbx

+0

@mbx: Sí, el método de Jon Skeet Times es mucho mejor cuando el índice no es necesario y solo quiere repetir una operación. –

29
foreach (var i in Enumerable.Range(0, N)) 
{ 
    // do something 
} 
+1

Esta versión es agradable, cuando necesita el índice. – mbx

+1

Sucinta y funciona: el índice siempre se puede ignorar si no es necesario. –

+2

Este es el que voy a hacer, ya que proporciona una manera sucinta pero clara de realizar la iteración. Siento que es mucho más compacto que la respuesta de Paul, aunque en realidad son la misma solución. – KGVT

10

veo Jon Skeet beat me to it, pero esta variación se permitirá pasar el índice a la acción cada vez es ejecutado:

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

y lo llaman así:

10.TimesWithIndex((i) => 
      obj[i].DoSomething()); 
3

Ejemplo 1

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

Ejemplo 2

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

Ejemplo 3

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

clase Loop & extensión

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); 
    } 

} 
0
while (i-->0) { 

} 

Mencionó que, si bien el bucle es peligroso, ya que puede ser infinito, la forma anterior es bastante simple y nunca será infinita. Al menos TOTALMENTE infinita :)

Es conveniente y corta (más corta que cualquiera de las otras respuestas) y funcionará exactamente i veces (porque los rendimientos de sufijo decremento valor antes de decremento).

+1

Se olvidó de la declaración de la variable y la inicialización que luego haría que su versión fuera inferior a la solicitada 'for (1..10)' y la respuesta seleccionada de 'int.Times' a través del método de extensión. – mbx

+0

De hecho, el mejor caso de uso para este escenario es cuando declaras/inicias el recuento de bucles en instrucciones previas usando alguna lógica más involucrada y aquí solo aplicas la lógica de bucle. – aaimnr

Cuestiones relacionadas