Подтвердить что ты не робот

Есть ли элегантный способ повторить действие?

В С#, используя .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();
});

Я знаю, что могу создать свой собственный метод расширения для первого примера, но нет ли существующей функции, которая делает это уже возможным?

4b9b3361

Ответ 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.