c# - ¿Hay una forma elegante de repetir una acción?
.net linq (8)
En C #, usando .NET Framework 4, ¿hay una forma elegante de repetir la misma acción un determinado número de veces? Por ejemplo, en lugar de:
int repeat = 10;
for (int i = 0; i < repeat; i++)
{
Console.WriteLine("Hello World.");
this.DoSomeStuff();
}
Me gustaría escribir algo como:
Action toRepeat = () =>
{
Console.WriteLine("Hello World.");
this.DoSomeStuff();
};
toRepeat.Repeat(10);
o:
Enumerable.Repeat(10, () =>
{
Console.WriteLine("Hello World.");
this.DoSomeStuff();
});
Sé que puedo crear mi propio método de extensión para el primer ejemplo, pero ¿no existe una característica existente que hace que ya sea posible hacer esto?
¿Me gusta esto?
using System.Linq;
Enumerable.Range(0, 10).ForEach(arg => toRepeat());
Esto ejecutará su método 10 veces.
[Editar]
Estoy tan acostumbrado a tener ForEach
método de extensión en Enumerable, que olvidé que no es parte de FCL.
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (var item in source)
action(item);
}
ForEach
es lo que puede hacer sin el método de extensión ForEach
:
Enumerable.Range(0, 10).ToList().ForEach(arg => toRepeat());
[Editar]
Creo que la solución más elegante es implementar un método reutilizable:
public static void RepeatAction(int repeatCount, Action action)
{
for (int i = 0; i < repeatCount; i++)
action();
}
Uso:
RepeatAction(10, () => { Console.WriteLine("Hello World."); });
Declara una extensión:
public static void Repeat(this Action action, int times){
while (times-- > 0)
action.Invoke();
}
Puedes usar el método de extensión como:
new Action(() =>
{
Console.WriteLine("Hello World.");
this.DoSomeStuff();
}).Repeat(10);
Lo escribí como un método de extensión Int32
. Al principio pensé que tal vez eso no suena como una buena idea, pero en realidad parece realmente genial cuando lo usas.
public static void Repeat(
this int count,
Action action
) {
for (int x = 0; x < count; x += 1)
action();
}
Uso:
5.Repeat(DoThing);
value.Repeat(() => queue.Enqueue(someItem));
(value1 - value2).Repeat(() => {
// complex implementation
});
Tenga en cuenta que no hará nada para los valores <= 0
. Al principio iba a lanzar negativos, pero siempre tendría que verificar qué número es mayor cuando comparo dos. Esto permite que la diferencia funcione si es positiva y no hace nada de lo contrario.
Podría escribir un método de extensión Abs
(ya sea value.Abs().Repeat(() => { });
o -5.RepeatAbs(() => { });
) si quisiera repetir independientemente del signo y luego el orden de la diferencia de dos números no importaría.
También son posibles otras variantes, como pasar el índice o proyectar en valores similares a Select
.
Sé que hay Enumerable.Range
pero esto es mucho más conciso.
No hay una forma incorporada de hacer esto.
La razón es que C # trata de forzar una división entre los lados funcional e imperativo del lenguaje. C # solo facilita la programación funcional cuando no va a producir efectos secundarios. Por lo tanto, obtienes métodos de manipulación de colecciones como Where
, Select
, etc. de LINQ, pero no obtienes ForEach
. 1
De manera similar, lo que estás tratando de hacer aquí es encontrar alguna forma funcional de expresar lo que es esencialmente una acción imperativa. Aunque C # le brinda las herramientas para hacer esto, no trata de hacerlo más fácil para usted, ya que hacerlo hace que su código no sea claro ni idiomático.
1 Hay una List<T>.ForEach
, pero no un IEnumerable<T>.ForEach
. Diría que la existencia de List<T>.ForEach
es un artefacto histórico derivado de que los diseñadores de frameworks no han pensado en estos problemas en la época de .NET 2.0; la necesidad de una división clara solo se hizo aparente en 3.0.
Por brevedad de un trazador de líneas que podría hacer esto. No estoy seguro de lo que piensas ...
Enumerable.Repeat<Action>(() =>
{
Console.WriteLine("Hello World.");
this.DoSomeStuff();
}, 10).ToList().ForEach(x => x());
Sin desplegar su propia extensión, supongo que puede hacer algo como esto
Action toRepeat = () => {
Console.WriteLine("Hello World.");
this.DoSomeStuff();
};
int repeat = 10;
Enumerable.Range(0, repeat).ToList().ForEach(i => toRepeat());
Enumerable.Repeat<Action>(() => { Console.WriteLine("Hello World"); this.DoSomething(); },10);
elegante ¿no?
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());
estas opciones no son elegantes debido a ToList (), pero ambas funcionaron en mi caso