Есть ли элегантный способ повторить действие?
В С#, используя .NET Framework 4, есть ли элегантный способ повторить одно и то же действие определенное количество раз? Например, вместо:
int repeat = 10;
for (int i = 0; i < repeat; i++)
{
Console.WriteLine("Hello World.");
this.DoSomeStuff();
}
Я хотел бы написать что-то вроде:
Action toRepeat = () =>
{
Console.WriteLine("Hello World.");
this.DoSomeStuff();
};
toRepeat.Repeat(10);
или
Enumerable.Repeat(10, () =>
{
Console.WriteLine("Hello World.");
this.DoSomeStuff();
});
Я знаю, что могу создать свой собственный метод расширения для первого примера, но нет ли существующей функции, которая делает это уже возможным?
Ответы
Ответ 1
Нет встроенного способа сделать это.
Причина в том, что С#, поскольку он пытается обеспечить разделение между функциональной и императивной сторонами языка. С# только упрощает выполнение функционального программирования, когда он не будет создавать побочные эффекты. Таким образом, вы получаете методы управления коллекцией, такие как LINQ Where
, Select
и т.д., Но вы не получаете ForEach
. 1
Аналогичным образом то, что вы пытаетесь сделать здесь, это найти функциональный способ выражения того, что по сути является императивным действием. Хотя С# дает вам инструменты для этого, он не пытается облегчить вам работу, так как это делает ваш код неясным и неидиоматичным.
1 Существует List<T>.ForEach
, но не IEnumerable<T>.ForEach
. Я бы сказал, что существование List<T>.ForEach
является историческим артефактом, связанным с дизайнерами фреймворков, которые не продумали эти проблемы во время .NET 2.0; потребность в четком разделении стала очевидной только в 3.0.
Ответ 2
Как это?
using System.Linq;
Enumerable.Range(0, 10).ForEach(arg => toRepeat());
Это выполнит ваш метод 10 раз.
[Редактировать]
Я так привык иметь метод расширения ForEach
на Enumerable, что я забыл, что он не является частью FCL.
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (var item in source)
action(item);
}
Вот что вы можете сделать без метода расширения ForEach
:
Enumerable.Range(0, 10).ToList().ForEach(arg => toRepeat());
[Редактировать]
Я думаю, что наиболее элегантным решением является реализация многоразового метода:
public static void RepeatAction(int repeatCount, Action action)
{
for (int i = 0; i < repeatCount; i++)
action();
}
Использование:
RepeatAction(10, () => { Console.WriteLine("Hello World."); });
Ответ 3
Для краткости одного лайнера вы можете это сделать. Не уверен, что вы думаете...
Enumerable.Repeat<Action>(() =>
{
Console.WriteLine("Hello World.");
this.DoSomeStuff();
}, 10).ToList().ForEach(x => x());
Ответ 4
Без развертывания собственного расширения, я думаю, вы можете сделать что-то вроде этого
Action toRepeat = () => {
Console.WriteLine("Hello World.");
this.DoSomeStuff();
};
int repeat = 10;
Enumerable.Range(0, repeat).ToList().ForEach(i => toRepeat());
Ответ 5
Enumerable.Repeat<Action>(() => { Console.WriteLine("Hello World"); this.DoSomething(); },10).ToList().ForEach(f => f.Invoke());
элегантно не так ли?
Ответ 6
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());
эти опции не элегантны из-за ToList(), но оба работали в моем случае
Ответ 7
Объявить расширение:
public static void Repeat(this Action action, int times){
while (times-- > 0)
action.Invoke();
}
Вы можете использовать метод расширения как:
new Action(() =>
{
Console.WriteLine("Hello World.");
this.DoSomeStuff();
}).Repeat(10);
Ответ 8
Я написал это как метод расширения Int32
. Сначала я подумал, что, может быть, это не очень хорошая идея, но на самом деле она кажется действительно великолепной, когда вы ее используете.
public static void Repeat(
this int count,
Action action
) {
for (int x = 0; x < count; x += 1)
action();
}
Использование:
5.Repeat(DoThing);
value.Repeat(() => queue.Enqueue(someItem));
(value1 - value2).Repeat(() => {
// complex implementation
});
Обратите внимание, что это ничего не даст для значений <= 0
. Сначала я собирался выбросить негативы, но потом мне всегда приходилось проверять, какое число больше при сравнении двух. Это позволяет разнице работать, если она положительна, и ничего не делать иначе.
Вы можете написать метод расширения Abs
(либо value.Abs().Repeat(() => { });
либо -5.RepeatAbs(() => { });
), если хотите повторить независимо от знака и тогда порядок разности двух чисел не имеет значения.
Возможны и другие варианты, такие как передача индекса или проецирование в значения, подобные Select
.
Я знаю, что есть Enumerable.Range
но это гораздо более кратко.
Ответ 9
Записать класс расширения int:
public static class IntExtensions
{
public static void RepeatAction(this int repeatCount, Action action)
{
if (repeatCount <= 0 || action == null)
return;
for (int counter = 0; counter < repeatCount; counter ++)
{
action.Invoke();
}
}
}
Используйте его таким образом:
Elements.Count.RepeatAction(() => DoFunnyStuff(parameter))
где Count - это int.